diff --git a/pom.xml b/pom.xml
index 53b785f..dec180c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -66,6 +66,10 @@
custom-portal-handler
provided
+
+ com.googlecode.json-simple
+ json-simple
+
org.gcube.applicationsupportlayer
aslcore
@@ -93,6 +97,11 @@
[2.13.0-SNAPSHOT, 3.0.0-SNAPSHOT)
compile
+
+ org.apache.httpcomponents
+ httpclient
+ 4.3.5
+
com.liferay.portal
portal-service
diff --git a/src/main/java/org/gcube/portlets/user/socialprofile/client/SocialProfile.java b/src/main/java/org/gcube/portlets/user/socialprofile/client/SocialProfile.java
index ed6625b..15dbd6f 100644
--- a/src/main/java/org/gcube/portlets/user/socialprofile/client/SocialProfile.java
+++ b/src/main/java/org/gcube/portlets/user/socialprofile/client/SocialProfile.java
@@ -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 onModuleLoad()
.
*/
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() {
+ @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() {
@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)
diff --git a/src/main/java/org/gcube/portlets/user/socialprofile/client/SocialService.java b/src/main/java/org/gcube/portlets/user/socialprofile/client/SocialService.java
index cad7057..e691341 100644
--- a/src/main/java/org/gcube/portlets/user/socialprofile/client/SocialService.java
+++ b/src/main/java/org/gcube/portlets/user/socialprofile/client/SocialService.java
@@ -15,4 +15,6 @@ public interface SocialService extends RemoteService {
Boolean saveHeadline(String newHeadline);
Boolean saveIsti(String institution);
+
+ Boolean fetchUserProfile(String authCode, String redirectURI);
}
diff --git a/src/main/java/org/gcube/portlets/user/socialprofile/client/SocialServiceAsync.java b/src/main/java/org/gcube/portlets/user/socialprofile/client/SocialServiceAsync.java
index 2137292..589cf54 100644
--- a/src/main/java/org/gcube/portlets/user/socialprofile/client/SocialServiceAsync.java
+++ b/src/main/java/org/gcube/portlets/user/socialprofile/client/SocialServiceAsync.java
@@ -11,5 +11,7 @@ public interface SocialServiceAsync {
void saveHeadline(String newHeadline, AsyncCallback callback);
void saveIsti(String institution, AsyncCallback callback);
+
+ void fetchUserProfile(String authCode, String redirectURI, AsyncCallback callback);
}
diff --git a/src/main/java/org/gcube/portlets/user/socialprofile/client/ui/DisplayProfile.java b/src/main/java/org/gcube/portlets/user/socialprofile/client/ui/DisplayProfile.java
index 112d37c..b25331c 100644
--- a/src/main/java/org/gcube/portlets/user/socialprofile/client/ui/DisplayProfile.java
+++ b/src/main/java/org/gcube/portlets/user/socialprofile/client/ui/DisplayProfile.java
@@ -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("&", "&").replaceAll("<", "<")
.replaceAll(">", ">");
- }
-
-
+ }
private String getRandomString() {
StringBuilder sb = new StringBuilder();
diff --git a/src/main/java/org/gcube/portlets/user/socialprofile/client/ui/ErrorAlert.java b/src/main/java/org/gcube/portlets/user/socialprofile/client/ui/ErrorAlert.java
index bd793e5..3855abe 100644
--- a/src/main/java/org/gcube/portlets/user/socialprofile/client/ui/ErrorAlert.java
+++ b/src/main/java/org/gcube/portlets/user/socialprofile/client/ui/ErrorAlert.java
@@ -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 {
}
- public ErrorAlert() {
- initWidget(uiBinder.createAndBindUi(this));
- }
- public ErrorAlert(String firstName) {
+
+ @UiField Element errorMessage;
+
+ public ErrorAlert(String message) {
initWidget(uiBinder.createAndBindUi(this));
+ errorMessage.setInnerText(message);
}
}
diff --git a/src/main/java/org/gcube/portlets/user/socialprofile/client/ui/ErrorAlert.ui.xml b/src/main/java/org/gcube/portlets/user/socialprofile/client/ui/ErrorAlert.ui.xml
index b3d8256..3e42dd9 100644
--- a/src/main/java/org/gcube/portlets/user/socialprofile/client/ui/ErrorAlert.ui.xml
+++ b/src/main/java/org/gcube/portlets/user/socialprofile/client/ui/ErrorAlert.ui.xml
@@ -14,6 +14,6 @@
}
- Import Error: it seems you denied our request to import from LinkedIn.
+ Import Error:
\ No newline at end of file
diff --git a/src/main/java/org/gcube/portlets/user/socialprofile/client/ui/OkAlert.java b/src/main/java/org/gcube/portlets/user/socialprofile/client/ui/OkAlert.java
index 05e6e98..74b7b4c 100644
--- a/src/main/java/org/gcube/portlets/user/socialprofile/client/ui/OkAlert.java
+++ b/src/main/java/org/gcube/portlets/user/socialprofile/client/ui/OkAlert.java
@@ -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 {
}
-
- public OkAlert() {
+ @UiField Element message;
+ public OkAlert(String message2Show) {
initWidget(uiBinder.createAndBindUi(this));
+ message.setInnerText(message2Show);
}
}
diff --git a/src/main/java/org/gcube/portlets/user/socialprofile/client/ui/OkAlert.ui.xml b/src/main/java/org/gcube/portlets/user/socialprofile/client/ui/OkAlert.ui.xml
index 71100dd..9a175a8 100644
--- a/src/main/java/org/gcube/portlets/user/socialprofile/client/ui/OkAlert.ui.xml
+++ b/src/main/java/org/gcube/portlets/user/socialprofile/client/ui/OkAlert.ui.xml
@@ -14,6 +14,6 @@
}
- Import operation completed successfully.
+
\ No newline at end of file
diff --git a/src/main/java/org/gcube/portlets/user/socialprofile/server/SocialServiceImpl.java b/src/main/java/org/gcube/portlets/user/socialprofile/server/SocialServiceImpl.java
index 977d499..7291e36 100644
--- a/src/main/java/org/gcube/portlets/user/socialprofile/server/SocialServiceImpl.java
+++ b/src/main/java/org/gcube/portlets/user/socialprofile/server/SocialServiceImpl.java
@@ -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("&", "&").replaceAll("<", "<")
.replaceAll(">", ">");
}
+ @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 params = new ArrayList(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 json = (Map) 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;
+ }
+ }
}
diff --git a/src/main/resources/clientlog4j.properties b/src/main/resources/clientlog4j.properties
new file mode 100644
index 0000000..fd367a5
--- /dev/null
+++ b/src/main/resources/clientlog4j.properties
@@ -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