everything in place for oAuth, just need to parse XML

git-svn-id: http://svn.research-infrastructures.eu/public/d4science/gcube/trunk/portlets/user/social-profile@99408 82a268e6-3cf1-43bd-a215-b396298e98cf
This commit is contained in:
Massimiliano Assante 2014-09-02 17:08:38 +00:00
parent 462549bf9b
commit 1069bf8229
11 changed files with 232 additions and 32 deletions

View File

@ -66,6 +66,10 @@
<artifactId>custom-portal-handler</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.googlecode.json-simple</groupId>
<artifactId>json-simple</artifactId>
</dependency>
<dependency>
<groupId>org.gcube.applicationsupportlayer</groupId>
<artifactId>aslcore</artifactId>
@ -93,6 +97,11 @@
<version>[2.13.0-SNAPSHOT, 3.0.0-SNAPSHOT)</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.3.5</version>
</dependency>
<dependency>
<groupId>com.liferay.portal</groupId>
<artifactId>portal-service</artifactId>

View File

@ -10,8 +10,10 @@ import org.gcube.portlets.user.socialprofile.shared.UserContext;
import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.Cookies;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.VerticalPanel;
@ -19,16 +21,45 @@ import com.google.gwt.user.client.ui.VerticalPanel;
* Entry point classes define <code>onModuleLoad()</code>.
*/
public class SocialProfile implements EntryPoint {
private final SocialServiceAsync socialService = GWT.create(SocialService.class);
private VerticalPanel mainPanel = new VerticalPanel();
private DisplayProfile dispProfile = new DisplayProfile();
public void onModuleLoad() {
if (isUserAuthZFromLinkedIn()) {
checkLinkedInAuthZ();
String authorizationCode = checkLinkedInAuthZ();
if (authorizationCode != null) {
mainPanel.add(new OkAlert("Authorization OK! Please wait while we import from LinkedIn ... "));
socialService.fetchUserProfile(authorizationCode, DisplayProfile.getRedirectURI(), new AsyncCallback<Boolean>() {
@Override
public void onSuccess(Boolean result) {
if (!result) {
mainPanel.clear();
mainPanel.add(new ErrorAlert("Something went wrong while parsing your professional summary from LinkedIn, please report the issue."));
}
else
Window.alert("funzia!");
}
@Override
public void onFailure(Throwable caught) {
mainPanel.clear();
mainPanel.add(new ErrorAlert("Something went wrong while communicating with LinkedIn service, please report us the issue."));
}
});
}
} else {
displayProfile();
}
RootPanel.get("SocialProfileDiv").add(mainPanel);
}
/**
* display the profile of the user
*/
private void displayProfile() {
socialService.getUserContext(getUserToShowId(), new AsyncCallback<UserContext>() {
@Override
public void onSuccess(UserContext result) {
@ -44,21 +75,27 @@ public class SocialProfile implements EntryPoint {
dispProfile.showError(caught.getMessage());
}
});
RootPanel.get("SocialProfileDiv").add(mainPanel);
}
private void checkLinkedInAuthZ() {
/**
*
* @return the token if everything goers ok, null otherwise
*/
private String checkLinkedInAuthZ() {
if (Window.Location.getParameter("error") != null) {
mainPanel.add(new ErrorAlert());
} else {
String code = Window.Location.getParameter("code");
String controlSequence = Window.Location.getParameter("state");
GWT.log("key="+code+" state="+controlSequence);
GWT.log("state="+controlSequence);
mainPanel.add(new OkAlert());
mainPanel.add(new ErrorAlert("it seems you denied our request to import your professional summary from LinkedIn."));
return null;
}
String code = Window.Location.getParameter("code");
String controlSequence = Window.Location.getParameter("state");
String cSeq2Compare = Cookies.getCookie(DisplayProfile.CONTROL_SEQUENCE_COOKIE);
if (controlSequence.compareTo(cSeq2Compare) != 0) {
mainPanel.add(new ErrorAlert("Something went wrong when importing your professional summary from LinkedIn, please try again."));
return null;
}
GWT.log("key="+code+" state="+controlSequence);
GWT.log("state="+controlSequence);
return code;
}
/**
@ -74,7 +111,7 @@ public class SocialProfile implements EntryPoint {
}
/**
*
* @return true if either the user
* @return true if the user has clicked import from LinkedIn
*/
private boolean isUserAuthZFromLinkedIn() {
if (Window.Location.getParameter("error") != null || Window.Location.getParameter("code") != null)

View File

@ -15,4 +15,6 @@ public interface SocialService extends RemoteService {
Boolean saveHeadline(String newHeadline);
Boolean saveIsti(String institution);
Boolean fetchUserProfile(String authCode, String redirectURI);
}

View File

@ -11,5 +11,7 @@ public interface SocialServiceAsync {
void saveHeadline(String newHeadline, AsyncCallback<Boolean> callback);
void saveIsti(String institution, AsyncCallback<Boolean> callback);
void fetchUserProfile(String authCode, String redirectURI, AsyncCallback<Boolean> callback);
}

View File

@ -17,6 +17,7 @@ import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.uibinder.client.UiHandler;
import com.google.gwt.user.client.Cookies;
import com.google.gwt.user.client.Random;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.Window.Location;
@ -44,8 +45,11 @@ public class DisplayProfile extends Composite {
public static final String loading = GWT.getModuleBaseURL() + "../images/avatarLoader.gif";
public static final String savingImage = GWT.getModuleBaseURL() + "../images/saving.gif";
public static final String GET_OID_PARAMETER = "oid";
public static final String CONTROL_SEQUENCE_COOKIE = "CSRF-check-d4science";
private final SocialServiceAsync socialService = GWT.create(SocialService.class);
@UiField HTMLPanel mainPanel;
@UiField Image avatarImage;
@UiField HTML userFullName;
@ -127,15 +131,20 @@ public class DisplayProfile extends Composite {
importButton.setVisible(true);
importButton.addClickHandler(new ClickHandler() {
//TODO: make it a runtime resource
@Override
public void onClick(ClickEvent event) {
String OAUTH2_SERVICE = "https://www.linkedin.com/uas/oauth2/authorization?response_type=code";
String D4S_APP_ID = "77n7r4c9nwuwk2";
String controlSequence = getRandomString();
//needed to prevent Cross Site Request Forgery attacks
Cookies.setCookie(CONTROL_SEQUENCE_COOKIE, controlSequence);
String url = OAUTH2_SERVICE + ""
+ "&client_id="+D4S_APP_ID
+ "&state="+controlSequence
+ "&redirect_uri="+Location.getHref();
+ "&redirect_uri="+getRedirectURI();
Location.assign(url);
}
@ -152,7 +161,18 @@ public class DisplayProfile extends Composite {
messageButton.setVisible(true);
}
}
/**
*
* @return the redirect uri when authorized (or not) by LinkedIn via oAuth2
*/
public static String getRedirectURI() {
String redirectURI = Window.Location.getProtocol()+"//"+Window.Location.getHost()+Window.Location.getPath();
//development case
if (Window.Location.getParameter("gwt.codesvr") != null)
return redirectURI+"?gwt.codesvr=127.0.0.1:9997";
return redirectURI;
}
@UiHandler("editHeadline")
void onEditHeadlineClick(ClickEvent e) {
headlineLabel.setVisible(false);
@ -309,9 +329,7 @@ public class DisplayProfile extends Composite {
}
return html.replaceAll("&", "&amp;").replaceAll("<", "&lt;")
.replaceAll(">", "&gt;");
}
}
private String getRandomString() {
StringBuilder sb = new StringBuilder();

View File

@ -1,7 +1,9 @@
package org.gcube.portlets.user.socialprofile.client.ui;
import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.Element;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.Widget;
@ -12,10 +14,11 @@ public class ErrorAlert extends Composite {
interface ErrorAlertUiBinder extends UiBinder<Widget, ErrorAlert> {
}
public ErrorAlert() {
initWidget(uiBinder.createAndBindUi(this));
}
public ErrorAlert(String firstName) {
@UiField Element errorMessage;
public ErrorAlert(String message) {
initWidget(uiBinder.createAndBindUi(this));
errorMessage.setInnerText(message);
}
}

View File

@ -14,6 +14,6 @@
}
</ui:style>
<g:HTMLPanel styleName="{style.alert}">
<span style="font-weight: bold;"> Import Error:</span> it seems you denied our request to import from LinkedIn.
<span style="font-weight: bold;"> Import Error: </span><span ui:field="errorMessage"></span>
</g:HTMLPanel>
</ui:UiBinder>

View File

@ -1,7 +1,9 @@
package org.gcube.portlets.user.socialprofile.client.ui;
import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.Element;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.Widget;
@ -11,9 +13,10 @@ public class OkAlert extends Composite {
interface OkAlertUiBinder extends UiBinder<Widget, OkAlert> {
}
public OkAlert() {
@UiField Element message;
public OkAlert(String message2Show) {
initWidget(uiBinder.createAndBindUi(this));
message.setInnerText(message2Show);
}
}

View File

@ -14,6 +14,6 @@
}
</ui:style>
<g:HTMLPanel styleName="{style.alert}">
Import operation completed successfully.
<span ui:field="message"></span>
</g:HTMLPanel>
</ui:UiBinder>

View File

@ -1,7 +1,23 @@
package org.gcube.portlets.user.socialprofile.server;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.gcube.application.framework.core.session.ASLSession;
import org.gcube.application.framework.core.session.SessionManager;
import org.gcube.portal.custom.communitymanager.OrganizationsUtil;
@ -9,12 +25,12 @@ import org.gcube.portal.custom.scopemanager.scopehelper.ScopeHelper;
import org.gcube.portal.databook.shared.UserInfo;
import org.gcube.portlets.user.socialprofile.client.SocialService;
import org.gcube.portlets.user.socialprofile.shared.UserContext;
import org.json.simple.parser.ContainerFactory;
import org.json.simple.parser.JSONParser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;
import com.liferay.portal.kernel.exception.PortalException;
import com.liferay.portal.kernel.exception.SystemException;
import com.liferay.portal.kernel.util.WebKeys;
import com.liferay.portal.model.User;
import com.liferay.portal.service.UserLocalServiceUtil;
@ -204,6 +220,98 @@ public class SocialServiceImpl extends RemoteServiceServlet implements SocialSer
return html.replaceAll("&", "&amp;").replaceAll("<", "&lt;")
.replaceAll(">", "&gt;");
}
@Override
public Boolean fetchUserProfile(String authCode, String redirectURI) {
try {
HttpClient httpClient = HttpClients.createDefault();
HttpPost httpPost = new HttpPost("https://www.linkedin.com/uas/oauth2/accessToken");
// Request parameters and other properties.
ArrayList<NameValuePair> params = new ArrayList<NameValuePair>(5);
params.add(new BasicNameValuePair("grant_type", "authorization_code"));
params.add(new BasicNameValuePair("code", authCode));
params.add(new BasicNameValuePair("redirect_uri", redirectURI));
params.add(new BasicNameValuePair("client_id", "xxx"));
params.add(new BasicNameValuePair("client_secret", "xxx"));
httpPost.setEntity(new UrlEncodedFormEntity(params, "UTF-8"));
//Execute and get the response.
HttpResponse httpResponse = httpClient.execute(httpPost);
HttpEntity entity = httpResponse.getEntity();
if (entity != null) {
InputStream myInputStream = entity.getContent();
try {
String jsonText = IOUtils.toString(myInputStream, "UTF-8");
_log.debug("LinkedIn response: " + jsonText);
if (jsonText == null)
return false;
JSONParser parser = new JSONParser();
@SuppressWarnings("rawtypes")
ContainerFactory containerFactory = new ContainerFactory(){
public List creatArrayContainer() {
return new LinkedList();
}
public Map createObjectContainer() {
return new LinkedHashMap();
}
};
@SuppressWarnings("unchecked")
Map<String, String> json = (Map<String, String>) parser.parse(jsonText, containerFactory);
if (json.get("error") != null)
return false;
String token = json.get("access_token");
if (token == null)
return false;
return parseProfile(httpClient, token);
}
finally {
myInputStream.close();
}
}
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
private boolean parseProfile(HttpClient httpClient, String token) {
// HttpGet request = new HttpGet("https://api.linkedin.com/v1/people/~");
HttpGet request = new HttpGet("https://api.linkedin.com/v1/people/~:(id,first-name,last-name,headline,summary,location:(name),industry)");
// add request header
request.addHeader("Authorization", "Bearer " + token);
try {
HttpResponse httpResponse = httpClient.execute(request);
HttpEntity entity = httpResponse.getEntity();
if (entity != null) {
InputStream myInputStream = entity.getContent();
try {
String xmlResponse = IOUtils.toString(myInputStream, "UTF-8");
_log.debug("LinkedIn xmlResponse: " + xmlResponse);
}
finally {
myInputStream.close();
}
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
}

View File

@ -0,0 +1,18 @@
log4j.rootLogger=DEBUG, A1
log4j.appender.A1=org.apache.log4j.ConsoleAppender
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
# Print the date in ISO 8601 format
log4j.appender.A1.layout.ConversionPattern=%d %-5p %c - %m%n
# Print only messages of level TRACE or above in the package org.gcube
log4j.logger.org.gcube=TRACE
log4j.logger.org.gcube.application.framework.core.session=INFO
log4j.logger.org.gcube.contentmanager=ERROR
log4j.logger.org.gcube.common.scope=ERROR
log4j.logger.org.gcube.contentmanagement=ERROR
log4j.logger.org.gcube.resources.discovery.icclient=ERROR
log4j.logger.org.gcube.common.clients=ERROR
log4j.logger.org.gcube.common.homelibrary.jcr=ERROR
log4j.logger.org.gcube.application.framework.accesslogger=ERROR
log4j.logger.org.apache.pdfbox.util.PDFStreamEngine=ERROR