001    package org.tynamo.security.federatedaccounts.base;
002    
003    import java.net.MalformedURLException;
004    import java.net.URI;
005    import java.util.ArrayList;
006    import java.util.List;
007    
008    import org.apache.http.HttpEntity;
009    import org.apache.http.HttpResponse;
010    import org.apache.http.HttpStatus;
011    import org.apache.http.NameValuePair;
012    import org.apache.http.client.HttpClient;
013    import org.apache.http.client.methods.HttpGet;
014    import org.apache.http.client.utils.URIUtils;
015    import org.apache.http.client.utils.URLEncodedUtils;
016    import org.apache.http.impl.client.DefaultHttpClient;
017    import org.apache.http.message.BasicNameValuePair;
018    import org.apache.http.params.BasicHttpParams;
019    import org.apache.http.util.EntityUtils;
020    import org.apache.shiro.SecurityUtils;
021    import org.apache.shiro.authc.AuthenticationException;
022    import org.apache.tapestry5.annotations.Component;
023    import org.apache.tapestry5.annotations.Environmental;
024    import org.apache.tapestry5.annotations.Property;
025    import org.apache.tapestry5.ioc.annotations.Inject;
026    import org.apache.tapestry5.ioc.annotations.Symbol;
027    import org.apache.tapestry5.services.BaseURLSource;
028    import org.apache.tapestry5.services.PageRenderLinkSource;
029    import org.apache.tapestry5.services.Request;
030    import org.apache.tapestry5.services.javascript.JavaScriptSupport;
031    import org.esxx.js.protocol.GAEConnectionManager;
032    import org.slf4j.Logger;
033    import org.tynamo.security.federatedaccounts.FederatedAccountSymbols;
034    import org.tynamo.security.federatedaccounts.components.FlashMessager;
035    import org.tynamo.security.federatedaccounts.oauth.FacebookAccessToken;
036    import org.tynamo.security.federatedaccounts.util.WindowMode;
037    
038    public abstract class AbstractFacebookOauthPage extends FacebookOauthComponentBase {
039            @Inject
040            @Symbol(FederatedAccountSymbols.HTTPCLIENT_ON_GAE)
041            private boolean httpClientOnGae;
042    
043            @Inject
044            @Symbol(FederatedAccountSymbols.SUCCESSURL)
045            private String successUrl;
046    
047            @Inject
048            private Logger logger;
049    
050            @Inject
051            private Request request;
052    
053            @Component
054            private FlashMessager flashMessager;
055    
056            @Inject
057            private PageRenderLinkSource linkSource;
058    
059            private boolean fbAuthenticated;
060    
061            @Property
062            private WindowMode windowMode;
063    
064            protected void onActivate(String windowModeText) throws MalformedURLException {
065                    try {
066                            windowMode = WindowMode.valueOf(windowModeText);
067                    } catch (IllegalArgumentException e) {
068                    }
069    
070                    String code = request.getParameter("code");
071                    if (code == null) {
072                            flashMessager.setFailureMessage("No Oauth authentication code provided");
073                            return;
074                    }
075    
076                    // String accessTokenUri = "https://graph.facebook.com/oauth/access_token?client_id=" + clientId
077                    // + "&redirect_uri=http://localhost:8080/oauth&client_secret=" + clientSecret + "&code=" + code;
078                    // String accessTokenUri = "https://graph.facebook.com/oauth/access_token";
079    
080                    // String accessTokenUri = "https://graph.facebook.com/oauth/access_token?client_id=" + clientId
081                    // + "&redirect_uri=http://localhost:8080/oauth&client_secret=" + clientSecret + "&code=" +
082                    // code;
083                    // https://graph.facebook.com/oauth/access_token?client_id=119507274749030&redirect_uri=http://localhost:8080/oauth&client_secret=d8b3b7dc6d5b6ddaebd68549002d643d&code=2._ViV33QBoP0Yhzmua_6gvA__.3600.1274994000-539598633|7_yFQgTwoBoD7DJYx8dqMhiXiP0.
084                    // logger.info("access token uri " + accessTokenUri);
085    
086                    List<NameValuePair> qparams = new ArrayList<NameValuePair>();
087                    qparams.add(new BasicNameValuePair("client_id", getOauthClientId()));
088                    qparams.add(new BasicNameValuePair("redirect_uri", getOauthRedirectLink(windowMode)));
089                    qparams.add(new BasicNameValuePair("client_secret", getOauthClientSecret()));
090                    qparams.add(new BasicNameValuePair("code", code));
091                    HttpGet get = null;
092                    String accessToken = "";
093                    long expires = 0L;
094                    try {
095                            URI uri = URIUtils
096                                            .createURI("https", "graph.facebook.com", -1, "/oauth/access_token", URLEncodedUtils.format(qparams, "UTF-8"), null);
097                            get = new HttpGet(uri);
098    
099                            // HttpGet get = new HttpGet(accessTokenUri);
100                            // NameValuePair[] queryString = new NameValuePair[4];
101                            // queryString[0] = new NameValuePair("client_id", clientId);
102                            // queryString[1] = new NameValuePair("redirect_uri", getOauthRedirectLink());
103                            // queryString[2] = new NameValuePair("client_secret", clientSecret);
104                            // queryString[3] = new NameValuePair("code", code);
105                            // get.setQueryString(queryString);
106                            // HttpClient httpClient = new DefaultHttpClient();
107                            HttpClient httpClient = httpClientOnGae ? new DefaultHttpClient(new GAEConnectionManager(), new BasicHttpParams())
108                                            : new DefaultHttpClient();
109    
110                            HttpResponse response = httpClient.execute(get);
111                            int status = response.getStatusLine().getStatusCode();
112                            if (HttpStatus.SC_OK != status) {
113                                    logger.error("Facebook access_token request returned status code " + status);
114                                    flashMessager.setFailureMessage("Facebook access_token request failed with status code: " + status);
115                                    return;
116                            }
117                            HttpEntity entity = response.getEntity();
118                            if (entity != null) {
119                                    long len = entity.getContentLength();
120                                    if (len != -1 && len < 2048) accessToken = EntityUtils.toString(entity);
121                            }
122                    } catch (Exception e) {
123                            logger.error("Facebook access_token request failed because of:", e);
124                            flashMessager.setFailureMessage("Facebook access_token request failed with message: " + e.getMessage());
125                            return;
126                    } finally {
127                            if (get != null) get.abort();
128                    }
129    
130                    try {
131                            if (!accessToken.startsWith("access_token")) throw new IllegalArgumentException();
132                            // access_token is of form:
133                            // access_token=119507274749030|2.1AptZFp9__qW3k2PuG4bVA__.3600.1274914800-539598633|9aTyryhVl8vnn3ulLy2w6Txo92E.&expires=4059
134                            accessToken = accessToken.substring(accessToken.indexOf("=") + 1);
135                            expires = Long.valueOf(accessToken.substring(accessToken.lastIndexOf("=") + 1));
136                            accessToken = accessToken.substring(0, accessToken.indexOf("&expires"));
137                    } catch (Exception e) {
138                            logger.error("access_token wasn't of right format");
139                            flashMessager.setFailureMessage("Facebook access_token wasn't of right format");
140                            return;
141                    }
142    
143                    try {
144                            SecurityUtils.getSubject().login(new FacebookAccessToken(accessToken, expires));
145                            flashMessager.setSuccessMessage("User successfully authenticated");
146                            fbAuthenticated = true;
147                    } catch (AuthenticationException e) {
148                            logger.error("Using access token " + accessToken + "\nCould not sign in a Facebook federated user because of: ", e);
149                            // FIXME Deal with other account exception types like expired and
150                            // locked
151                            flashMessager.setFailureMessage("A Facebook federated user cannot be signed in, report this to support.\n " + e.getMessage());
152                    }
153            }
154    
155            @Inject
156            private BaseURLSource baseURLSource;
157    
158            public String getSuccessLink() {
159                    return "".equals(successUrl) ? "" : baseURLSource.getBaseURL(request.isSecure()) + successUrl;
160            }
161    
162            @Environmental
163            private JavaScriptSupport javaScriptSupport;
164    
165            protected void afterRender() {
166                    if (fbAuthenticated) javaScriptSupport.addScript("onAuthenticationSuccess('" + getSuccessLink() + "', '" + windowMode.name() + "');");
167            }
168    }