com.ublish.service.BasicLTIService.java Source code

Java tutorial

Introduction

Here is the source code for com.ublish.service.BasicLTIService.java

Source

package com.ublish.service;

import java.io.IOException;
import java.net.URISyntaxException;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

import net.oauth.OAuthException;
import net.oauth.OAuthMessage;
import net.oauth.signature.OAuthSignatureMethod;

import org.apache.commons.lang.StringUtils;
import org.joda.time.DateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import blackboard.data.content.Content;
import blackboard.data.course.Course;
import blackboard.data.course.CourseMembership;
import blackboard.data.course.CourseMembership.Role;
import blackboard.data.registry.CourseRegistryUtil;
import blackboard.data.registry.SystemRegistryUtil;
import blackboard.data.role.RoleUtil;
import blackboard.data.user.User;
import blackboard.persist.Id;
import blackboard.platform.LicenseManagerFactory;
import blackboard.platform.branding.BrandingUtil;
import blackboard.platform.branding.PersonalStyleHelper;
import blackboard.platform.context.Context;
import blackboard.platform.gradebook2.impl.GradableItemDAO;
import blackboard.platform.intl.BbLocale;
import blackboard.platform.intl.LocaleManagerFactory;
import blackboard.platform.plugin.PlugInUtil;
import blackboard.platform.proxytool.ProxyToolConstants;
import blackboard.platform.proxytool.ProxyToolUtil;
import blackboard.util.Base64Codec;
import blackboard.util.GeneralUtil;
import blackboard.util.LocaleUtil;
import blackboard.util.UrlUtil;
import blackboard.util.UuidFactory;

import com.google.common.base.Joiner;
import com.google.common.collect.Lists;
import com.ublish.data.CustomParameters;
import com.ublish.data.LaunchParameters;
import com.ublish.data.LtiLaunch;
import com.ublish.outcomes.consumer.LtiOutcomesSourcedIdResolver;

@Service("basicLTIService")
public class BasicLTIService {
    private static final Logger logger = LoggerFactory.getLogger(BasicLTIService.class);

    private static final String UTF8 = "UTF-8";

    @Autowired
    private String b2Vendor;

    @Autowired
    private String b2Handle;

    @Autowired
    private String urlRegistryKey;

    @Autowired
    private String urlDefaultValue;

    @Autowired
    private String courseKeyRegistryKey;

    @Autowired
    private String courseSecretRegistryKey;

    public LtiLaunch buildToolLaunch(Context context, Course course, User user, CourseMembership membership)
            throws OAuthException {
        String consumerKey = CourseRegistryUtil.getString(course.getId(), courseKeyRegistryKey, "");
        String consumerSecret = CourseRegistryUtil.getString(course.getId(), courseSecretRegistryKey, "");

        if (StringUtils.isBlank(consumerKey) || StringUtils.isBlank(consumerSecret)) {
            throw new OAuthException(
                    "Invalid Credentials: Please enter the Consumer Key and Secret for your myBusinessCourse course.");
        }

        LtiLaunch launch = new LtiLaunch();
        launch.setLaunchParameters(addLaunchParameters(context, course, user, membership, null));
        launch.setCustomParameters(addCustomParameters(context, course, null));
        launch.setHttpMethod("POST");
        launch.setUrl(SystemRegistryUtil.getString(urlRegistryKey, urlDefaultValue));
        launch.setConsumerKey(consumerKey);
        launch.setConsumerSecret(consumerSecret);

        addOAuthParametersAndSign(launch);

        return launch;
    }

    public LtiLaunch buildContentLaunch(Context context, Course course, User user, CourseMembership membership,
            Content content) throws OAuthException {
        String consumerKey = CourseRegistryUtil.getString(course.getId(), courseKeyRegistryKey, "");
        String consumerSecret = CourseRegistryUtil.getString(course.getId(), courseSecretRegistryKey, "");

        if (StringUtils.isBlank(consumerKey) || StringUtils.isBlank(consumerSecret)) {
            if (!membership.getRole().equals(Role.INSTRUCTOR)) {
                throw new OAuthException(
                        "Invalid Credentials: The credentials for thsi course are invalid. Please ask your instructor to enter the Consumer Key and Consumer Secret for your course on the myBusinessCourse Tools Page.");
            } else {
                throw new OAuthException(
                        "Invalid Credentials: Please enter the Consumer Key and Secret for your course on the myBusinessCourse Tools page.");
            }
        }

        LtiLaunch launch = new LtiLaunch();
        launch.setLaunchParameters(addLaunchParameters(context, course, user, membership, content));
        launch.setCustomParameters(addCustomParameters(context, course, content));
        launch.setHttpMethod("POST");
        launch.setUrl(content.getUrl());
        launch.setConsumerKey(consumerKey);
        launch.setConsumerSecret(consumerSecret);

        addOAuthParametersAndSign(launch);

        return launch;
    }

    private void addOAuthParametersAndSign(LtiLaunch launch) throws OAuthException {
        addOAuthParameters(launch);

        OAuthMessage message = new OAuthMessage(launch.getHttpMethod(), launch.getUrl(),
                Lists.newArrayList(launch.getParameters().entrySet()));
        String signature = getSignature(message, launch.getConsumerSecret());

        logger.debug("OAuth Signature: {}", signature);
        launch.getLaunchParameters().setOauthSignature(signature);
    }

    private void addOAuthParameters(LtiLaunch launch) {
        LaunchParameters launchParameters = launch.getLaunchParameters();
        launchParameters.setOauthCallback(LaunchParameters.OAUTH_CALLBACK_DEFAULT);
        launchParameters.setOauthConsumerKey(launch.getConsumerKey());
        launchParameters.setOauthNonce(getNonce());
        launchParameters.setOauthSignatureMethod(LaunchParameters.OAUTH_SIGNATURE_METHOD_HMACSHA1);
        launchParameters.setOauthTimestamp(getTimestamp());
        launchParameters.setOauthVersion(LaunchParameters.OAUTH_VERSION_DEFAULT);
    }

    private LaunchParameters addLaunchParameters(Context context, Course course, User user,
            CourseMembership membership, Content content) {
        LaunchParameters launchParameters = new LaunchParameters();
        // LTI Params
        launchParameters.setLtiVersion(LaunchParameters.BASIC_LTI_VERSION);
        launchParameters.setLtiMessageType(LaunchParameters.BASIC_LTI_MESSAGE_TYPE);

        //Resource
        if (content != null) {
            launchParameters.setResourceLinkId(content.getId().getExternalString());

            if (!StringUtils.isBlank(content.getTitle())) {
                launchParameters.setResourceLinkTitle(content.getTitle());
            }

            String description = content.getBody().getFormattedText();
            if (!StringUtils.isBlank(description)) {
                launchParameters.setResourceLinkDescription(description);
            }
        } else {
            // add a random value as resourceLinkId.
            launchParameters.setResourceLinkId(UuidFactory.createUuid());
        }

        //Tool Consumer
        launchParameters.setToolConsumerInstanceGuid(GeneralUtil.getSystemInstallationId());

        String instanceName = GeneralUtil.getSystemInstanceName();
        if (!StringUtils.isBlank(instanceName)) {
            launchParameters.setToolConsumerInstanceName(instanceName);
        }

        String sysadminEmail = GeneralUtil.getSystemAdminEmail();
        if (!StringUtils.isEmpty(sysadminEmail)) {
            launchParameters.setToolConsumerInstanceContactEmail(sysadminEmail);
        }

        launchParameters.setToolConsumerInfoCode("Blackboard Learn");
        launchParameters.setToolConsumerInfoVersion(GeneralUtil.getReleaseNumber());
        launchParameters.setExtLms(String.format("bb-%s", LicenseManagerFactory.getInstance().getBuildNumber()));

        BbLocale locale = LocaleManagerFactory.getInstance().getLocale();
        launchParameters.setLaunchPresentationLocale(locale.getLocale().replace('_', '-'));

        launchParameters.setLaunchPresentationReturnUrl(getBaseUrl(context));
        launchParameters.setExtLaunchPresentationCssUrl(getCssUrls(context, course, user));

        // User
        launchParameters.setUserId(user.getUuid());
        launchParameters.setLisPersonNameGiven(user.getGivenName());
        launchParameters.setLisPersonNameFamily(user.getFamilyName());
        // use trim to fix a bug in the core product where there is always a leading space in the user name.
        launchParameters.setLisPersonNameFull(locale.formatName(user, BbLocale.Name.LONG).trim());
        if (!StringUtils.isEmpty(user.getEmailAddress())) {
            launchParameters.setLisPersonContactEmailPrimary(user.getEmailAddress());
        }

        String ltiRole = ProxyToolUtil.getInstance().getLtiRole(user, membership,
                ProxyToolConstants.LTI_VERSION_BASIC_LTI);
        launchParameters.setRoles(ltiRole);

        // Context
        launchParameters.setContextId(course.getUuid());
        launchParameters.setContextLabel(course.getCourseId());
        launchParameters.setContextTitle(course.getTitle());

        // Content
        if (content != null && content.getLaunchInNewWindow()) {
            launchParameters.setLaunchPresentationDocumentTarget("window");
        } else {
            launchParameters.setLaunchPresentationDocumentTarget("frame");
        }

        addGradingParameters(membership, content, launchParameters);

        return launchParameters;
    }

    private void addGradingParameters(CourseMembership courseMembership, Content content,
            LaunchParameters launchParameters) {

        if (content == null) {
            // Skip this for tool launches.
            logger.debug("No content item found in the launch. Returning...");
            return;
        }

        Id gradableItemId = GradableItemDAO.get().getIdFromContentId(content.getId());
        if (Id.isValid(gradableItemId)) {
            // Only set this if the current user is a student - no other roles actually have grading results.
            String url = UrlUtil.calculateFullUrl(PlugInUtil.getUri(b2Vendor, b2Handle, "app/grading/lti11grade"));
            String sourcedId = null;
            if (RoleUtil.isStudentRole(courseMembership.getRole())) {
                sourcedId = LtiOutcomesSourcedIdResolver.toSourcedId(courseMembership.getId(), gradableItemId);
                launchParameters.setLisResultSourcedId(sourcedId);
            }
            // Change in conformance test: To support the use case of 'instructor is first user to access link
            // and tool wants to change instructor UI based on knowledge that grading is supported on the link'
            // we will now send the gradingurl for all roles, not just the student role (and passing null to 
            // addGradingInformation for the sourcedId accomplishes that)

            launchParameters.setLisOutcomeServiceUrl(url);
        }
    }

    private CustomParameters addCustomParameters(Context context, Course course, Content content) {
        CustomParameters customParameters = new CustomParameters();
        customParameters.setBlackboardCourseId(course.getUuid());
        customParameters.setBlackboardUrl(getBaseUrl(context));
        customParameters.setBlackboardB2Uri(PlugInUtil.getUri(b2Vendor, b2Handle, ""));

        if (content != null) {
            Map<String, String> customContentParameters = content.getExtendedData().getValues();
            for (Entry<String, String> entry : customContentParameters.entrySet()) {
                customParameters.setParameter(entry.getKey(), entry.getValue());
            }
        }

        return customParameters;
    }

    private String getBaseUrl(Context context) {
        return UrlUtil.calculateFullUrl(context.getRequest(), "");
    }

    private String getCssUrls(Context context, Course course, User user) {
        List<String> cssUrls = BrandingUtil.getCssUrls(context.getRequest(), user, course, null,
                PersonalStyleHelper.isHighContrast(context.getRequest()), !LocaleUtil.isLeftToRight());
        return Joiner.on(",").skipNulls().join(cssUrls);
    }

    private String getNonce() {
        return UuidFactory.createUuid();
    }

    private String getTimestamp() {
        return Long.toString(new DateTime().getMillis() / 1000);
    }

    private String getBaseString(OAuthMessage message) throws URISyntaxException, IOException {
        return OAuthSignatureMethod.getBaseString(message);
    }

    private String getSignature(OAuthMessage message, String secret) throws OAuthException {
        try {
            String baseString = getBaseString(message);

            byte[] keyBytes = (secret + "&").getBytes(UTF8);

            SecretKey secretKey = new SecretKeySpec(keyBytes, LaunchParameters.OAUTH_SIGNATURE_METHOD_HMACSHA1);

            Mac mac = Mac.getInstance("HmacSHA1");
            mac.init(secretKey);

            return Base64Codec.encode(mac.doFinal(baseString.getBytes(UTF8))).trim();
        } catch (Exception e) {
            throw new OAuthException("An exception occurred while creating the OAuth signature.", e);
        }
    }
}