package auth.login;
import gamification.ExecuteAction;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.util.HashMap;
import java.util.Map;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.oltu.oauth2.client.OAuthClient;
import org.apache.oltu.oauth2.client.URLConnectionClient;
import org.apache.oltu.oauth2.client.request.OAuthClientRequest;
import org.apache.oltu.oauth2.client.response.GitHubTokenResponse;
import org.apache.oltu.oauth2.client.response.OAuthAccessTokenResponse;
import org.apache.oltu.oauth2.client.response.OAuthAuthzResponse;
import org.apache.oltu.oauth2.client.response.OAuthJSONAccessTokenResponse;
import org.apache.oltu.oauth2.common.OAuth;
import org.apache.oltu.oauth2.common.exception.OAuthProblemException;
import org.apache.oltu.oauth2.common.exception.OAuthSystemException;
import org.apache.oltu.oauth2.common.message.types.GrantType;
import org.apache.oltu.oauth2.common.utils.OAuthUtils;
import org.apache.oltu.oauth2.jwt.JWT;
import org.apache.oltu.oauth2.jwt.io.JWTClaimsSetWriter;
import org.apache.oltu.oauth2.jwt.io.JWTHeaderWriter;
import org.apache.oltu.openidconnect.client.response.OpenIdConnectResponse;
import persistency.entities.LoggedUser;
import persistency.exposed.LoggedUserExposed;
import persistency.exposed.UserInfoJson;
import persistency.exposed.LoggedUserExposed.FacebookUserInfoJson;
import persistency.exposed.LoggedUserExposed.LinkedInUserInfoJson;
import service.rest.wrappers.OidClaimSetJsonObject;
import twitter4j.Twitter;
import twitter4j.TwitterException;
import twitter4j.auth.AccessToken;
import twitter4j.auth.RequestToken;
import utils.LoginUtils;
import auth.openidconnect.ApplicationException;
import auth.openidconnect.OAuthParams;
import auth.openidconnect.ProviderData;
import auth.openidconnect.Utils;
import com.google.gson.Gson;
/**
* Servlet implementation class RedirectServlet
*/
public class RedirectServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
LoggedUser user = processRequest(request, response);
ExecuteAction.getInstance().execute("login", user, null);
}
private LoggedUser processRequest(HttpServletRequest request,
HttpServletResponse response) throws IOException, ServletException {
LoggedUser user = null;
if(isAlreadyAuthenticatedThroughClient(request)){
return authThroughClient(request, response);
} else {
Twitter twitter = (Twitter) request.getSession().getAttribute(Utils.TWITTER);
String provider = null;
if(twitter != null){
provider = Utils.TWITTER;
RequestToken requestToken = (RequestToken) request.getSession().getAttribute("requestToken");
String verifier = request.getParameter("oauth_verifier");
if(verifier == null){
(new Logout()).performLocalInfoRemove(request, response);
response.sendRedirect("/");
} else {
try {
AccessToken authAccessToken = twitter.getOAuthAccessToken(requestToken, verifier);
request.getSession().removeAttribute("requestToken");
user = initOrCreateUserTwitter(request, response, twitter, authAccessToken);
} catch (TwitterException e) {
(new Logout()).performLocalInfoRemove(request, response);
response.sendRedirect("/");
// throw new ServletException(e);
}
}
} else{
OAuthParams oauthParams = new OAuthParams();
try {
// Get OAuth Info
String clientId = LoginUtils.findCookieValue(request, "clientId");
String clientSecret = LoginUtils.findCookieValue(request, "clientSecret");
String authzEndpoint = LoginUtils.findCookieValue(request, "authzEndpoint");
String tokenEndpoint = LoginUtils.findCookieValue(request, "tokenEndpoint");
String redirectUri = LoginUtils.findCookieValue(request, "redirectUri");
String scope = LoginUtils.findCookieValue(request, "scope");
String state = LoginUtils.findCookieValue(request, "state");
oauthParams.setClientId(clientId);
oauthParams.setClientSecret(clientSecret);
oauthParams.setAuthzEndpoint(authzEndpoint);
oauthParams.setTokenEndpoint(tokenEndpoint);
oauthParams.setRedirectUri(redirectUri);
oauthParams.setScope(Utils.isIssued(scope));
oauthParams.setState(Utils.isIssued(state));
// Create the response wrapper
OAuthAuthzResponse oar = null;
oar = OAuthAuthzResponse.oauthCodeAuthzResponse(request);
// Get Authorization Code
String code = oar.getCode();
oauthParams.setAuthzCode(code);
String app = LoginUtils.findCookieValue(request, "app");
response.addCookie(new Cookie("app", app));
provider = app;
oauthParams.setApplication(app);
user = initToken(oauthParams, request, response);
} catch (OAuthProblemException e) {
StringBuffer sb = new StringBuffer();
sb.append("</br>");
sb.append("Error code: ").append(e.getError()).append("</br>");
sb.append("Error description: ").append(e.getDescription()).append("</br>");
sb.append("Error uri: ").append(e.getUri()).append("</br>");
sb.append("State: ").append(e.getState()).append("</br>");
oauthParams.setErrorMessage(sb.toString());
// throw new ServletException(sb.toString(), e);
e.printStackTrace();
response.sendRedirect("/");
} catch (OAuthSystemException e) {
e.printStackTrace();
response.sendRedirect("/");
// throw new ServletException(e);
}
}
createLoginCookie(response, provider);
response.sendRedirect(request.getContextPath() + "/");
}
return user;
}
private void createLoginCookie(HttpServletResponse response, String provider) {
response.addCookie(new Cookie(Utils.COOKIE_PROVIDER_NAME, provider));
}
private LoggedUser initOrCreateUserTwitter(HttpServletRequest request,
HttpServletResponse response, Twitter twitter, AccessToken authAccessToken) throws TwitterException, IOException {
String screenName = twitter.getScreenName();
// long id = twitter.getId();
UserInfoJson result = new UserInfoJson();
result.setEmail(screenName);
result.setName(screenName+"");
result.setAccessToken(authAccessToken.getToken());
result.setSecretAccessToken(authAccessToken.getTokenSecret());
LoggedUserExposed lue = new LoggedUserExposed();
LoggedUser findPersonByOpenId = lue.createNewUser(Utils.TWITTER, result);
//backup 60 min
findPersonByOpenId.setSessionExpires(System.currentTimeMillis()+60*60*1000);
lue.updateEntity(findPersonByOpenId);
LoginUtils.createCookie(response, Utils.TWITTER);
request.getSession().setAttribute(Utils.ACCESS_TOKEN_SESSION_KEY, findPersonByOpenId.getAccessToken());
return findPersonByOpenId;
}
private LoggedUser initToken(OAuthParams oauthParams,
HttpServletRequest req, HttpServletResponse response) throws OAuthSystemException, IOException, ServletException {
try {
Utils.validateTokenParams(oauthParams, req.getServerName());
OAuthClientRequest request = OAuthClientRequest
.tokenLocation(oauthParams.getTokenEndpoint())
.setClientId(oauthParams.getClientId())
.setClientSecret(oauthParams.getClientSecret())
.setRedirectURI(oauthParams.getRedirectUri())
.setCode(oauthParams.getAuthzCode())
.setGrantType(GrantType.AUTHORIZATION_CODE).setParameter(OAuth.OAUTH_ACCESS_TOKEN, oauthParams.getAccessToken())
.buildBodyMessage();
URLConnectionClient httpClient = new URLConnectionClient();
OAuthClient client = new OAuthClient(httpClient);
String app = LoginUtils.findCookieValue(req, "app");
OAuthAccessTokenResponse oauthResponse = null;
Class<? extends OAuthAccessTokenResponse> cl = OAuthJSONAccessTokenResponse.class;
if (Utils.FACEBOOK.equalsIgnoreCase(app)) {
cl = GitHubTokenResponse.class;
} else if (Utils.GOOGLE.equalsIgnoreCase(app)){
cl = OpenIdConnectResponse.class;
}
// initCACerts(app, req);
oauthResponse = client.accessToken(request, cl);
oauthParams.setAccessToken(oauthResponse.getAccessToken());
//dirty workaround
if(Utils.FACEBOOK.equalsIgnoreCase(app)){
String[] bodySplit = oauthResponse.getBody().split("&");
if(bodySplit != null){
for (String line : bodySplit) {
if(line != null && line.startsWith("expires=")){
oauthParams.setExpiresIn(Long.parseLong(line.substring("expires=".length()))*1000);
break;
}
}
}
} else {
oauthParams.setExpiresIn(oauthResponse.getExpiresIn());
}
oauthParams.setRefreshToken(Utils.isIssued(oauthResponse.getRefreshToken()));
if (Utils.GOOGLE.equalsIgnoreCase(app)){
fetchUserDataFromGoogle(oauthParams, oauthResponse);
}
return initOrCreateUser(req, response, oauthParams);
} catch (ApplicationException e) {
oauthParams.setErrorMessage(e.getMessage());
throw new ServletException(e);
} catch (OAuthProblemException e) {
StringBuffer sb = new StringBuffer();
sb.append("</br>");
sb.append("Error code: ").append(e.getError()).append("</br>");
sb.append("Error description: ").append(e.getDescription()).append("</br>");
sb.append("Error uri: ").append(e.getUri()).append("</br>");
sb.append("State: ").append(e.getState()).append("</br>");
oauthParams.setErrorMessage(sb.toString());
throw new ServletException(sb.toString());
}
}
private void initCACerts(String app, HttpServletRequest req) {
KeyStore trustStore;
try {
trustStore = KeyStore.getInstance("JKS");
trustStore.load(req.getServletContext().getResourceAsStream(app+".jks"), "123456".toCharArray());
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
KeyManager[] kms = kmf.getKeyManagers();
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(trustStore);
TrustManager[] tms = tmf.getTrustManagers();
SSLContext sslContext = null;
sslContext = SSLContext.getInstance("TLS");
sslContext.init(kms, tms, new SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
} catch (Exception e) {
e.printStackTrace();
}
}
private void fetchUserDataFromGoogle(OAuthParams oauthParams,
OAuthAccessTokenResponse oauthResponse)
throws MalformedURLException, ServletException {
OpenIdConnectResponse openIdConnectResponse = ((OpenIdConnectResponse)oauthResponse);
JWT idToken = openIdConnectResponse.getIdToken();
oauthParams.setIdToken(idToken.getRawString());
oauthParams.setHeader(new JWTHeaderWriter().write(idToken.getHeader()));
oauthParams.setClaimsSet(new JWTClaimsSetWriter().write(idToken.getClaimsSet()));
URL url = new URL(oauthParams.getTokenEndpoint());
oauthParams.setIdTokenValid(openIdConnectResponse.checkId(url.getHost(), oauthParams.getClientId()));
if(!oauthParams.isIdTokenValid()){
throw new ServletException("Failed to authenticate");
}
}
private LoggedUser initOrCreateUser(HttpServletRequest request,
HttpServletResponse response, OAuthParams oauthParams) throws IOException {
LoggedUserExposed lue = new LoggedUserExposed();
Gson g = new Gson();
LoggedUser findPersonByOpenId = null;
OidClaimSetJsonObject fromJson = null;
if (oauthParams.getClaimsSet() != null){
fromJson = g.fromJson(oauthParams.getClaimsSet(), OidClaimSetJsonObject.class);
findPersonByOpenId = lue.findPersonByOpenId(lue.assebleOpenId(fromJson.getEmail(), oauthParams.getApplication()));
}
if(findPersonByOpenId == null){
String userInfoString = null;
String userEmail = null;
try {
ProviderData data = Utils.getProvider(oauthParams.getApplication(), request);
userInfoString = getUserInfoString(oauthParams, data.getUserInfoEndpoint());
if(data.getUserEmailEndpoint() != null){
userEmail = getUserInfoString(oauthParams, data.getUserEmailEndpoint());
}
} catch (OAuthSystemException e) {
//NO user info can be retrieved
e.printStackTrace();
}
UserInfoJson userInfoJson = null;
if(userInfoString != null && userInfoString.length() > 0){
if(oauthParams.getApplication().equalsIgnoreCase(Utils.GOOGLE)){
userInfoJson = g.fromJson(userInfoString, UserInfoJson.class);
// if(userInfoJson.getEmail() == null && userInfoJson.getId() != null){
// userInfoJson.setEmail(userInfoJson.getId());
// }
} else if(oauthParams.getApplication().equalsIgnoreCase(Utils.LINKEDIN)){
userInfoJson = assembleUserInfo(g, userInfoString, userEmail);
} else if(oauthParams.getApplication().equalsIgnoreCase(Utils.FACEBOOK)){
userInfoJson = assembleUserInfoFromFacebook(g, userInfoString);
}
}
userInfoJson.setAccessToken(oauthParams.getAccessToken());
if(userInfoJson.getEmail()== null){
throw new IllegalArgumentException();
}
findPersonByOpenId = lue.createNewUser(oauthParams.getApplication(), userInfoJson);
}
if(fromJson != null){
findPersonByOpenId.setSessionExpires(System.currentTimeMillis() + Long.parseLong(fromJson.getExp()));
} else if(oauthParams.getExpiresIn() != null) {
findPersonByOpenId.setSessionExpires(System.currentTimeMillis() + oauthParams.getExpiresIn());
} else {
//backup 60 min
findPersonByOpenId.setSessionExpires(System.currentTimeMillis() + 60*60*1000);
}
findPersonByOpenId.setAccessToken(oauthParams.getAccessToken());
LoginUtils.createCookie(response, oauthParams.getApplication());
lue.updateEntity(findPersonByOpenId);
request.getSession().setAttribute(Utils.ACCESS_TOKEN_SESSION_KEY, findPersonByOpenId.getAccessToken());
return findPersonByOpenId;
}
private UserInfoJson assembleUserInfo(Gson g, String userInfoString,
String userEmail) {
LinkedInUserInfoJson userInfoJspon = g.fromJson(userInfoString, LinkedInUserInfoJson.class);
UserInfoJson result = new UserInfoJson();
result.setGiven_name(userInfoJspon.getFirstName());
result.setFamili_name(userInfoJspon.getLastName());
result.setEmail(userEmail);
return result;
}
private UserInfoJson assembleUserInfoFromFacebook(Gson g, String userInfoString) {
FacebookUserInfoJson userInfoJson = g.fromJson(userInfoString, FacebookUserInfoJson.class);
UserInfoJson result = new UserInfoJson();
result.setGiven_name(userInfoJson.getFirstName());
result.setFamili_name(userInfoJson.getLastName());
result.setName(userInfoJson.getName());
result.setEmail(userInfoJson.getEmail());
return result;
}
private String getUserInfoString(OAuthParams oauthParams, String userInfoEndpoint) throws OAuthSystemException {
OAuthClientRequest request = OAuthClientRequest
.tokenLocation(userInfoEndpoint)
.setClientId(oauthParams.getClientId())
.setClientSecret(oauthParams.getClientSecret())
.setRedirectURI(oauthParams.getRedirectUri())
.setCode(oauthParams.getAuthzCode())
.setGrantType(GrantType.AUTHORIZATION_CODE).setParameter(OAuth.OAUTH_ACCESS_TOKEN, oauthParams.getAccessToken())
.buildQueryMessage();
URLConnection c = null;
int responseCode = 0;
Map<String, String> headers = new HashMap<String, String>();
headers.put(OAuth.HeaderType.CONTENT_TYPE, OAuth.ContentType.URL_ENCODED);
if(oauthParams.getApplication().equalsIgnoreCase(Utils.LINKEDIN)){
headers.put(OAuth.HeaderType.AUTHORIZATION, "Bearer "+ oauthParams.getAccessToken());
headers.put("x-li-format", "json");
}
try {
URL url = new URL(request.getLocationUri());
c = url.openConnection();
responseCode = -1;
if (c instanceof HttpURLConnection) {
HttpURLConnection httpURLConnection = (HttpURLConnection)c;
if (headers != null && !headers.isEmpty()) {
for (Map.Entry<String, String> header : headers.entrySet()) {
httpURLConnection.addRequestProperty(header.getKey(), header.getValue());
}
}
if (request.getHeaders() != null) {
for (Map.Entry<String, String> header : request.getHeaders().entrySet()) {
httpURLConnection.addRequestProperty(header.getKey(), header.getValue());
}
}
httpURLConnection.setRequestMethod(OAuth.HttpMethod.GET);
httpURLConnection.connect();
InputStream inputStream;
responseCode = httpURLConnection.getResponseCode();
if (responseCode == 400 || responseCode == 401) {
inputStream = httpURLConnection.getErrorStream();
} else {
inputStream = httpURLConnection.getInputStream();
}
return OAuthUtils.saveStreamAsString(inputStream);
}
} catch (IOException e) {
throw new OAuthSystemException(e);
}
return null;
}
private LoggedUser authThroughClient(HttpServletRequest request, HttpServletResponse response) throws IOException{
OAuthParams oauthParams = new OAuthParams();
oauthParams.setAccessToken(request.getHeader(Utils.ACCESS_TOKEN_SESSION_KEY));
oauthParams.setApplication(request.getHeader("provider"));
return initOrCreateUser(request, response, oauthParams);
}
private boolean isAlreadyAuthenticatedThroughClient(HttpServletRequest request){
return request.getHeader(Utils.ACCESS_TOKEN_SESSION_KEY) != null && request.getHeader("provider") != null;
}
}