Index: lams_common/src/java/org/lamsfoundation/lams/dbupdates/patch20190806.sql
===================================================================
diff -u
--- lams_common/src/java/org/lamsfoundation/lams/dbupdates/patch20190806.sql (revision 0)
+++ lams_common/src/java/org/lamsfoundation/lams/dbupdates/patch20190806.sql (revision 2e1574cede0a43314af36aa790c3ca156845cab2)
@@ -0,0 +1,14 @@
+-- Turn off autocommit, so nothing is committed if there is an error
+SET AUTOCOMMIT = 0;
+SET FOREIGN_KEY_CHECKS=0;
+----------------------Put all sql statements below here-------------------------
+
+-- LDEV-4589 Add column for LTI Membership service
+ALTER TABLE lams_ext_server_org_map ADD COLUMN `membership_url` text COLLATE utf8mb4_unicode_ci;
+
+----------------------Put all sql statements above here-------------------------
+
+-- If there were no errors, commit and restore autocommit to on
+COMMIT;
+SET AUTOCOMMIT = 1;
+SET FOREIGN_KEY_CHECKS=1;
\ No newline at end of file
Index: lams_common/src/java/org/lamsfoundation/lams/integration/ExtServer.java
===================================================================
diff -u -r5b1fbc89c883857eace8c043a594a79cb0f60bc4 -r2e1574cede0a43314af36aa790c3ca156845cab2
--- lams_common/src/java/org/lamsfoundation/lams/integration/ExtServer.java (.../ExtServer.java) (revision 5b1fbc89c883857eace8c043a594a79cb0f60bc4)
+++ lams_common/src/java/org/lamsfoundation/lams/integration/ExtServer.java (.../ExtServer.java) (revision 2e1574cede0a43314af36aa790c3ca156845cab2)
@@ -59,6 +59,9 @@
@Column(name = "ext_groups_url")
private String extGroupsUrl;
+
+ @Column(name = "membership_url")
+ private String membershipUrl;
@Column
private Boolean disabled;
@@ -196,7 +199,15 @@
public void setExtGroupsUrl(String extGroupsUrl) {
this.extGroupsUrl = extGroupsUrl;
}
+
+ public String getMembershipUrl() {
+ return this.membershipUrl;
+ }
+ public void setMembershipUrl(String membershipUrl) {
+ this.membershipUrl = membershipUrl;
+ }
+
public Boolean getDisabled() {
return this.disabled;
}
Index: lams_common/src/java/org/lamsfoundation/lams/integration/service/IIntegrationService.java
===================================================================
diff -u -ra5bb8ab1bbc5f6732acef6132286e89c80f2e8f3 -r2e1574cede0a43314af36aa790c3ca156845cab2
--- lams_common/src/java/org/lamsfoundation/lams/integration/service/IIntegrationService.java (.../IIntegrationService.java) (revision a5bb8ab1bbc5f6732acef6132286e89c80f2e8f3)
+++ lams_common/src/java/org/lamsfoundation/lams/integration/service/IIntegrationService.java (.../IIntegrationService.java) (revision 2e1574cede0a43314af36aa790c3ca156845cab2)
@@ -23,9 +23,12 @@
package org.lamsfoundation.lams.integration.service;
+import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.List;
+import org.apache.http.client.ClientProtocolException;
+import org.imsglobal.lti.launch.LtiSigningException;
import org.lamsfoundation.lams.integration.ExtCourseClassMap;
import org.lamsfoundation.lams.integration.ExtServerLessonMap;
import org.lamsfoundation.lams.integration.ExtServer;
@@ -117,7 +120,18 @@
* @return
*/
ExtServerLessonMap getLtiConsumerLesson(String serverId, String resourceLinkId);
-
+
+ /**
+ * Get the memberships.
+ *
+ * @param string $role Role for which memberships are to be requested (optional, default is all roles)
+ * @param int $limit Limit on the number of memberships to be returned (optional, default is all)
+ *
+ * @return mixed The array of User objects if successful, otherwise false
+ */
+ void addExtUsersToLesson(ExtServer extServer, Long lessonId, String courseId, String resourceLinkId)
+ throws IOException, UserInfoFetchException, UserInfoValidationException;
+
ExtServerLessonMap getExtServerLessonMap(Long lessonId);
/**
@@ -224,6 +238,10 @@
ExtCourseClassMap getExtCourseClassMap(Integer sid, Long lessonId);
+ ExtUserUseridMap addExtUserToLesson(ExtServer extServer, String method, String lsIdStr, String username, String firstName,
+ String lastName, String email, String courseId, String countryIsoCode, String langIsoCode)
+ throws UserInfoFetchException, UserInfoValidationException;
+
/**
* Creates an external org and normal org. It does not set roles for the creator.
*/
Index: lams_common/src/java/org/lamsfoundation/lams/integration/service/IntegrationService.java
===================================================================
diff -u -ra5bb8ab1bbc5f6732acef6132286e89c80f2e8f3 -r2e1574cede0a43314af36aa790c3ca156845cab2
--- lams_common/src/java/org/lamsfoundation/lams/integration/service/IntegrationService.java (.../IntegrationService.java) (revision a5bb8ab1bbc5f6732acef6132286e89c80f2e8f3)
+++ lams_common/src/java/org/lamsfoundation/lams/integration/service/IntegrationService.java (.../IntegrationService.java) (revision 2e1574cede0a43314af36aa790c3ca156845cab2)
@@ -42,7 +42,18 @@
import java.util.Map;
import org.apache.commons.lang.StringUtils;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.ClientProtocolException;
+import org.apache.http.client.HttpResponseException;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.util.EntityUtils;
import org.apache.log4j.Logger;
+import org.imsglobal.lti.BasicLTIConstants;
+import org.imsglobal.lti.launch.LtiOauthSigner;
+import org.imsglobal.lti.launch.LtiSigner;
+import org.imsglobal.lti.launch.LtiSigningException;
import org.imsglobal.pox.IMSPOXRequest;
import org.lamsfoundation.lams.gradebook.GradebookUserLesson;
import org.lamsfoundation.lams.gradebook.service.IGradebookService;
@@ -57,6 +68,7 @@
import org.lamsfoundation.lams.integration.security.RandomPasswordGenerator;
import org.lamsfoundation.lams.integration.util.GroupInfoFetchException;
import org.lamsfoundation.lams.integration.util.LoginRequestDispatcher;
+import org.lamsfoundation.lams.integration.util.LtiUtils;
import org.lamsfoundation.lams.lesson.Lesson;
import org.lamsfoundation.lams.lesson.service.ILessonService;
import org.lamsfoundation.lams.timezone.service.ITimezoneService;
@@ -78,10 +90,10 @@
import org.lamsfoundation.lams.util.WebUtil;
import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import oauth.signpost.exception.OAuthException;
-
/**
*
* View Source
@@ -857,7 +869,49 @@
List list = service.findByProperties(ExtCourseClassMap.class, properties);
return list == null || list.isEmpty() ? null : list.get(0);
}
+
+ @Override
+ public ExtUserUseridMap addExtUserToLesson(ExtServer extServer, String method, String lsIdStr, String username,
+ String firstName, String lastName, String email, String courseId, String countryIsoCode, String langIsoCode)
+ throws UserInfoFetchException, UserInfoValidationException {
+ if (log.isDebugEnabled()) {
+ log.debug("Adding user '" + username + "' as " + method + " to lesson with id '" + lsIdStr + "'.");
+ }
+
+ ExtUserUseridMap userMap = null;
+ if ((firstName == null) && (lastName == null)) {
+ userMap = getExtUserUseridMap(extServer, username);
+ } else {
+ final boolean usePrefix = true;
+ final boolean isUpdateUserDetails = false;
+ userMap = getImplicitExtUserUseridMap(extServer, username, firstName, lastName, langIsoCode, countryIsoCode,
+ email, usePrefix, isUpdateUserDetails);
+ }
+
+ // ExtUserUseridMap userMap = integrationService.getExtUserUseridMap(extServer, username);
+ // adds user to group
+ ExtCourseClassMap orgMap = getExtCourseClassMap(extServer, userMap, courseId, null, method);
+ //TODO when merging to newer branch, check and maybe change to the following
+// getExtCourseClassMap(extServer, userMap, courseId, countryIsoCode, langIsoCode, null,
+// method);
+
+ User user = userMap.getUser();
+ if (user == null) {
+ String error = "Unable to add user to lesson class as user is missing from the user map";
+ log.error(error);
+ throw new UserInfoFetchException(error);
+ }
+
+ if (LoginRequestDispatcher.METHOD_LEARNER.equals(method)) {
+ lessonService.addLearner(Long.parseLong(lsIdStr), user.getUserId());
+ } else if (LoginRequestDispatcher.METHOD_MONITOR.equals(method)) {
+ lessonService.addStaffMember(Long.parseLong(lsIdStr), user.getUserId());
+ }
+
+ return userMap;
+ }
+
@Override
public ExtServerLessonMap getLtiConsumerLesson(String serverId, String resourceLinkId) {
Map properties = new HashMap<>();
@@ -898,7 +952,211 @@
return (ExtCourseClassMap) list.get(0);
}
}
+
+ @Override
+ public void addExtUsersToLesson(ExtServer extServer, Long lessonId, String courseId, String resourceLinkId)
+ throws IOException, UserInfoFetchException, UserInfoValidationException {
+
+// $isLink = is_a($this->source, 'IMSGlobal\LTI\ToolProvider\ResourceLink');
+
+ log.info("addExtUsersToLesson invoked. params: courseId " + courseId + "resourceLinkId " + resourceLinkId);
+ JsonNode json = send(extServer, resourceLinkId);
+ if (json == null) {
+// $users = false;
+ } else {
+
+// $users = array();
+// if ($isLink) {
+// $oldUsers = $this->source->getUserResultSourcedIDs(true, ToolProvider\ToolProvider::ID_SCOPE_RESOURCE);
+// }
+
+ JsonNode memberships = json.get("pageOf").get("membershipSubject").get("membership");
+ for (int i = 0; i < memberships.size(); i++) {
+ JsonNode membership = memberships.get(i);
+ log.info("membership" + i + ": " + membership.toString());
+
+ JsonNode member = membership.get("member");
+ String extUserId = member.get("userId").asText();
+ String firstName = member.get("givenName").asText();
+ String lastName = member.get("familyName").asText();
+ String fullName = member.get("name").asText();
+ String email = member.get("email").asText();
+ // Set the user name and email
+ String[] defaultLangCountry = LanguageUtil.getDefaultLangCountry();
+
+ String countryIsoCode = defaultLangCountry[1] ;
+ String langIsoCode = defaultLangCountry[0];
+// ExtUserUseridMap extUser = getImplicitExtUserUseridMap(extServer, extUserId, firstName, lastName, defaultLangCountry[1],
+// defaultLangCountry[0], email, StringUtils.isNotBlank(extServer.getPrefix()), true);
+ //?? getUserDataFromExtServer
+
+
+// Set the user roles
+ JsonNode jsonRoles = membership.get("role");
+ log.info("membership" + i + " roles: " + jsonRoles.toString());
+ String roles = new String();
+ for (int j = 0; j < jsonRoles.size(); j++) {
+ String role = jsonRoles.get(j).asText();
+ roles += role + ",";
+ }
+
+ String method;
+ ExtUserUseridMap extUser;
+ if (LtiUtils.isStaff(roles, extServer) || LtiUtils.isAdmin(roles)) {
+ log.info("Adding user as monitor");
+ method = LoginRequestDispatcher.METHOD_MONITOR;
+
+ extUser = addExtUserToLesson(extServer, method, lessonId.toString(), extUserId, firstName, lastName, email, courseId, countryIsoCode, langIsoCode);
+
+ } else {
+ log.info("Adding user as learner");
+ method = LoginRequestDispatcher.METHOD_LEARNER;
+ extUser = addExtUserToLesson(extServer, method, lessonId.toString(), extUserId, firstName, lastName, email, courseId, countryIsoCode, langIsoCode);
+ }
+
+// if (isset($membership->role)) {
+// if (!is_array($roles)) {
+// $roles = explode(',', $roles);
+// }
+// $parsedRoles = array();
+// foreach ($roles as $role) {
+// $role = trim($role);
+// if (!empty($role)) {
+// if (substr($role, 0, 4) !== 'urn:') {
+// $role = 'urn:lti:role:ims/lis/' . $role;
+// }
+// $parsedRoles[] = $role;
+// }
+// }
+// $user->roles = roles;
+// }
+
+// If a result sourcedid is provided save the user
+
+
+
+ JsonNode messages = membership.get("message");
+ log.info("membership" + i + " messages: " + messages.toString());
+ if (messages != null && messages.size() > 0) {
+ for (int k = 0; k < messages.size(); k++) {
+ JsonNode message = messages.get(k);
+ String messageType = message.get("message_type").asText();
+ String tcGradebookId = message.get(BasicLTIConstants.LIS_RESULT_SOURCEDID) == null ? ""
+ : message.get(BasicLTIConstants.LIS_RESULT_SOURCEDID).asText();
+
+ if (StringUtils.isNotBlank(messageType) && "basic-lti-launch-request".equals(messageType)) {
+ if (StringUtils.isNotBlank(tcGradebookId)) {
+ log.info("User setTcGradebookId tcGradebookId:" + tcGradebookId);
+ extUser.setTcGradebookId(tcGradebookId);
+ service.save(extUser);
+ }
+ break;
+ }
+ }
+ }
+
+// Remove old user (if it exists)
+// if ($isLink) {
+// unset($oldUsers[$user->getId(ToolProvider\ToolProvider::ID_SCOPE_RESOURCE)]);
+// }
+ }
+
+// Delete any old users which were not in the latest list from the tool consumer
+// if ($isLink) {
+// foreach ($oldUsers as $id => $user) {
+// $user->delete();
+// }
+// }
+ }
+ }
+ /**
+ * Send a service request.
+ *
+ * @param array
+ * $parameters Query parameters to add to endpoint (optional, default is none)
+ *
+ * @return HTTPMessage HTTP object containing request and response details
+ * @throws LtiSigningException
+ * @throws IOException
+ * @throws ClientProtocolException
+ */
+ public JsonNode send(ExtServer extServer, String resourceLinkId) throws IOException {
+
+ String membershipUrl = extServer.getMembershipUrl();
+ //if tool consumer haven't provided membershipUrl (ToolProxyBinding.memberships.url parameter) we can't add any users
+ if (StringUtils.isBlank(membershipUrl)) {
+ return null;
+ }
+
+ membershipUrl += membershipUrl.contains("?") ? "&" : "?";
+ membershipUrl += "rlid=" + resourceLinkId;
+
+ HttpGet ltiServiceGetRequest = new HttpGet(membershipUrl);
+ ltiServiceGetRequest.setHeader("Accept", "application/vnd.ims.lis.v2.membershipcontainer+json");
+// request.setEntity(new StringEntity(xml, "UTF-8"));
+// ltiServiceGetRequest.setAdditionalParameters(parameters);
+// if (empty($data)) {
+// if (!empty($type)) {
+// $header .= "\nAccept: {$type}";
+// }
+// } else if (isset($type)) {
+// $header .= "\nContent-Type: {$type}";
+// $header .= "\nContent-Length: " . strlen($data);
+// }
+
+ log.info("Call membershipUrl:" + membershipUrl);
+
+// BasicLTIUtil.validateMessage(request, URL, oauth_secret)
+
+ LtiSigner ltiSigner = new LtiOauthSigner();
+ try {
+ HttpRequest httpRequest = ltiSigner.sign(ltiServiceGetRequest, extServer.getServerid(),
+ extServer.getServerkey());
+ } catch (LtiSigningException e) {
+ throw new RuntimeException(e);
+ }
+
+ DefaultHttpClient client = new DefaultHttpClient();
+ HttpResponse response = client.execute(ltiServiceGetRequest);
+ if (response.getStatusLine().getStatusCode() >= 400) {
+ throw new HttpResponseException(response.getStatusLine().getStatusCode(),
+ response.getStatusLine().getReasonPhrase());
+ }
+
+ String responseString = EntityUtils.toString(response.getEntity());
+ log.info("Response: " + responseString);
+ JsonNode actualObj = new ObjectMapper().readTree(responseString);
+
+// JSONObject jsonObject = new JSONObject(EntityUtils.toString(response.getEntity()));
+ return actualObj;
+// Gson gson = new GsonBuilder().create();
+// responseJSON.put("contributeActivities", new JSONArray(gson.toJson(contributeActivities)));
+
+// LtiVerifier verifier = new LtiOauthVerifier();
+// LtiVerificationResult result = verifier.verify(ltiServiceGetRequest, extServer.getServerkey());
+
+
+
+
+// if (!$this->unsigned) {
+// $header = ToolProvider\ToolConsumer::addSignature(membershipUrl, $this->consumer->getKey(), $this->consumer->secret, body, "GET", $this->mediaType);
+// } else {
+// $header = null;
+// }
+
+ // Connect to tool consumer
+// $http = new HTTPMessage(membershipUrl, "GET", body, $header);
+ // Parse JSON response
+// if ($http->send() && !empty($http->response)) {
+// $http->responseJson = json_decode($http->response);
+// $http->ok = !is_null($http->responseJson);
+// }
+//
+// return $http;
+
+ }
+
// ---------------------------------------------------------------------
// Inversion of Control Methods - Method injection
// ---------------------------------------------------------------------
Index: lams_common/src/java/org/lamsfoundation/lams/integration/util/LtiUtils.java
===================================================================
diff -u -r7475d08afc280b5e2e5ddf04e8bf35e3166aaf80 -r2e1574cede0a43314af36aa790c3ca156845cab2
--- lams_common/src/java/org/lamsfoundation/lams/integration/util/LtiUtils.java (.../LtiUtils.java) (revision 7475d08afc280b5e2e5ddf04e8bf35e3166aaf80)
+++ lams_common/src/java/org/lamsfoundation/lams/integration/util/LtiUtils.java (.../LtiUtils.java) (revision 2e1574cede0a43314af36aa790c3ca156845cab2)
@@ -4,6 +4,8 @@
import java.util.LinkedList;
import java.util.List;
+import org.lamsfoundation.lams.integration.ExtServer;
+
public class LtiUtils {
public static final String OAUTH_CONSUMER_KEY = "oauth_consumer_key";
@@ -32,12 +34,17 @@
*
* @return true
if the user has a role of instructor, contentdeveloper or teachingassistant
*/
- public static boolean isStaff(String roles) {
+ public static boolean isStaff(String roles, ExtServer extServer) {
List rolesToSearchFor = new LinkedList();
rolesToSearchFor.add("urn:lti:role:ims/lis/Instructor");
rolesToSearchFor.add("urn:lti:role:ims/lis/ContentDeveloper");
rolesToSearchFor.add("urn:lti:role:ims/lis/TeachingAssistant");
+ String toolConsumerMonitorRoles = extServer.getLtiToolConsumerMonitorRoles();
+ if (toolConsumerMonitorRoles != null) {
+ rolesToSearchFor.addAll(Arrays.asList(toolConsumerMonitorRoles.split(",")));
+ }
+
return hasRole(roles, rolesToSearchFor);
}