Merge pull request #3038 from jfederico/master
bbb-lti: Implemented improvements to the LTI integration
This commit is contained in:
commit
603fa151c1
@ -32,12 +32,15 @@ bigbluebuttonSalt=bbb_salt
|
||||
# e.g. localhost or localhost:port
|
||||
ltiEndPoint=localhost
|
||||
# The list of consumers allowed to access this lti service.
|
||||
# Format: {consumerId1:sharedSecret1}
|
||||
# Format: {consumerId1:sharedSecret1,consumerId2:sharedSecret2,consumerIdN:sharedSecretN}
|
||||
##ltiConsumers=bbb:bbb_salt
|
||||
ltiConsumers=bbb:welcome
|
||||
# The mode used to interact with BigBlueButton
|
||||
# Format: [<simple>|extended]
|
||||
ltiMode=extended
|
||||
# Defines if LTI credentials are required
|
||||
# Format: [false|<true>]
|
||||
ltiRestrictedAccess=true
|
||||
|
||||
#----------------------------------------------------
|
||||
# Inject configuration values into BigbluebuttonSrvice beans
|
||||
@ -49,4 +52,5 @@ beans.bigbluebuttonService.salt=${bigbluebuttonSalt}
|
||||
beans.ltiService.endPoint=${ltiEndPoint}
|
||||
beans.ltiService.consumers=${ltiConsumers}
|
||||
beans.ltiService.mode=${ltiMode}
|
||||
beans.ltiService.restrictedAccess=${ltiRestrictedAccess}
|
||||
|
||||
|
@ -54,17 +54,26 @@ class ToolController {
|
||||
ltiService.logParameters(params)
|
||||
|
||||
if( request.post ){
|
||||
def endPoint = getScheme() + "://" + ltiService.endPoint + "/" + grailsApplication.metadata['app.name'] + "/" + params.get("controller") + (params.get("format") != null? "." + params.get("format"): "")
|
||||
def scheme = request.isSecure()? "https": "http"
|
||||
def endPoint = scheme + "://" + ltiService.endPoint + "/" + grailsApplication.metadata['app.name'] + "/" + params.get("controller") + (params.get("format") != null? "." + params.get("format"): "")
|
||||
log.info "endPoint: " + endPoint
|
||||
Map<String, String> result = new HashMap<String, String>()
|
||||
ArrayList<String> missingParams = new ArrayList<String>()
|
||||
|
||||
if (hasAllRequiredParams(params, missingParams)) {
|
||||
def sanitizedParams = sanitizePrametersForBaseString(params)
|
||||
def consumer = ltiService.getConsumer(params.get(Parameter.CONSUMER_ID))
|
||||
if (consumer != null) {
|
||||
log.debug "Found consumer with key " + consumer.get("key") //+ " and sharedSecret " + consumer.get("secret")
|
||||
if (checkValidSignature(params.get(REQUEST_METHOD), endPoint, consumer.get("secret"), sanitizedParams, params.get(Parameter.OAUTH_SIGNATURE))) {
|
||||
log.debug "The message has a valid signature."
|
||||
if ( !ltiService.hasRestrictedAccess() || consumer != null) {
|
||||
if (ltiService.hasRestrictedAccess() ) {
|
||||
log.debug "Found consumer with key " + consumer.get("key") //+ " and sharedSecret " + consumer.get("secret")
|
||||
}
|
||||
|
||||
if (!ltiService.hasRestrictedAccess() || checkValidSignature(params.get(REQUEST_METHOD), endPoint, consumer.get("secret"), sanitizedParams, params.get(Parameter.OAUTH_SIGNATURE))) {
|
||||
if (!ltiService.hasRestrictedAccess() ) {
|
||||
log.debug "Access not restricted, valid signature is not required."
|
||||
} else {
|
||||
log.debug "The message has a valid signature."
|
||||
}
|
||||
|
||||
def mode = params.containsKey(Parameter.CUSTOM_MODE)? params.get(Parameter.CUSTOM_MODE): ltiService.mode
|
||||
if( !"extended".equals(mode) ) {
|
||||
@ -77,11 +86,13 @@ class ToolController {
|
||||
result = doJoinMeeting(params)
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
log.debug "The message has NOT a valid signature."
|
||||
result.put("resultMessageKey", "InvalidSignature")
|
||||
result.put("resultMessage", "Invalid signature (" + params.get(Parameter.OAUTH_SIGNATURE) + ").")
|
||||
}
|
||||
|
||||
} else {
|
||||
result.put("resultMessageKey", "ConsumerNotFound")
|
||||
result.put("resultMessage", "Consumer with id = " + params.get(Parameter.CONSUMER_ID) + " was not found.")
|
||||
@ -311,12 +322,12 @@ class ToolController {
|
||||
log.debug "Checking for required parameters"
|
||||
|
||||
boolean hasAllParams = true
|
||||
if ( !params.containsKey(Parameter.CONSUMER_ID) ) {
|
||||
if ( ltiService.hasRestrictedAccess() && !params.containsKey(Parameter.CONSUMER_ID) ) {
|
||||
missingParams.add(Parameter.CONSUMER_ID);
|
||||
hasAllParams = false;
|
||||
}
|
||||
|
||||
if ( !params.containsKey(Parameter.OAUTH_SIGNATURE)) {
|
||||
if ( ltiService.hasRestrictedAccess() && !params.containsKey(Parameter.OAUTH_SIGNATURE)) {
|
||||
missingParams.add(Parameter.OAUTH_SIGNATURE);
|
||||
hasAllParams = false;
|
||||
}
|
||||
@ -341,22 +352,27 @@ class ToolController {
|
||||
private boolean checkValidSignature(String method, String url, String conSecret, Properties postProp, String signature) {
|
||||
def validSignature = false
|
||||
|
||||
try {
|
||||
OAuthMessage oam = new OAuthMessage(method, url, postProp.entrySet())
|
||||
//log.debug "OAuthMessage oam = " + oam.toString()
|
||||
if ( ltiService.hasRestrictedAccess() ) {
|
||||
try {
|
||||
OAuthMessage oam = new OAuthMessage(method, url, postProp.entrySet())
|
||||
//log.debug "OAuthMessage oam = " + oam.toString()
|
||||
|
||||
HMAC_SHA1 hmac = new HMAC_SHA1()
|
||||
//log.debug "HMAC_SHA1 hmac = " + hmac.toString()
|
||||
HMAC_SHA1 hmac = new HMAC_SHA1()
|
||||
//log.debug "HMAC_SHA1 hmac = " + hmac.toString()
|
||||
|
||||
hmac.setConsumerSecret(conSecret)
|
||||
hmac.setConsumerSecret(conSecret)
|
||||
|
||||
log.debug "Base Message String = [ " + hmac.getBaseString(oam) + " ]\n"
|
||||
String calculatedSignature = hmac.getSignature(hmac.getBaseString(oam))
|
||||
log.debug "Calculated: " + calculatedSignature + " Received: " + signature
|
||||
log.debug "Base Message String = [ " + hmac.getBaseString(oam) + " ]\n"
|
||||
String calculatedSignature = hmac.getSignature(hmac.getBaseString(oam))
|
||||
log.debug "Calculated: " + calculatedSignature + " Received: " + signature
|
||||
|
||||
validSignature = calculatedSignature.equals(signature)
|
||||
} catch( Exception e ) {
|
||||
log.debug "Exception error: " + e.message
|
||||
validSignature = calculatedSignature.equals(signature)
|
||||
} catch( Exception e ) {
|
||||
log.debug "Exception error: " + e.message
|
||||
}
|
||||
|
||||
} else {
|
||||
validSignature = true
|
||||
}
|
||||
|
||||
return validSignature
|
||||
@ -398,18 +414,4 @@ class ToolController {
|
||||
|
||||
return cartridge
|
||||
}
|
||||
|
||||
private String getScheme(){
|
||||
def scheme
|
||||
if ( request.isSecure() ) {
|
||||
scheme = 'https'
|
||||
} else {
|
||||
scheme = request.getHeader("scheme")
|
||||
if ( scheme == null || !(scheme == 'http' || scheme == 'https') ) {
|
||||
scheme = 'http'
|
||||
}
|
||||
}
|
||||
|
||||
return scheme
|
||||
}
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ import java.util.Map;
|
||||
|
||||
import javax.crypto.spec.SecretKeySpec
|
||||
import javax.crypto.Mac
|
||||
|
||||
import org.apache.commons.codec.binary.Base64
|
||||
|
||||
class LtiService {
|
||||
@ -30,8 +31,7 @@ class LtiService {
|
||||
def endPoint = "localhost"
|
||||
def consumers = "demo:welcome"
|
||||
def mode = "simple"
|
||||
|
||||
def ssl_enabled
|
||||
def restrictedAccess = "true"
|
||||
|
||||
Map<String, String> consumerMap
|
||||
|
||||
@ -92,45 +92,55 @@ class LtiService {
|
||||
return Base64.encodeBase64URLSafeString(signBytes)
|
||||
}
|
||||
|
||||
def logParameters(Object params) {
|
||||
log.debug "----------------------------------"
|
||||
for( param in params ) log.debug "${param.getKey()}=${param.getValue()}"
|
||||
log.debug "----------------------------------"
|
||||
def logParameters(Object params, boolean debug = false) {
|
||||
def divider = "----------------------------------"
|
||||
Map<String, String> ordered_params = new LinkedHashMap<String, String>(params)
|
||||
ordered_params = ordered_params.sort {it.key}
|
||||
if( debug ) log.debug divider else log.info divider
|
||||
for( param in ordered_params ) {
|
||||
if( debug ) {
|
||||
log.debug "${param.getKey()}=${param.getValue()}"
|
||||
} else {
|
||||
log.info "${param.getKey()}=${param.getValue()}"
|
||||
}
|
||||
}
|
||||
if( debug ) log.debug divider else log.info divider
|
||||
}
|
||||
|
||||
def boolean isSSLEnabled(String query) {
|
||||
if ( ssl_enabled == null ) {
|
||||
ssl_enabled = false
|
||||
log.debug("Pinging SSL connection")
|
||||
def ssl_enabled = false
|
||||
|
||||
try {
|
||||
// open connection
|
||||
StringBuilder urlStr = new StringBuilder(query)
|
||||
URL url = new URL(urlStr.toString())
|
||||
HttpURLConnection httpConnection = (HttpURLConnection) url.openConnection()
|
||||
httpConnection.setUseCaches(false)
|
||||
httpConnection.setDoOutput(true)
|
||||
httpConnection.setRequestMethod("HEAD")
|
||||
httpConnection.setConnectTimeout(5000)
|
||||
httpConnection.connect()
|
||||
log.debug("Pinging SSL connection")
|
||||
try {
|
||||
// open connection
|
||||
StringBuilder urlStr = new StringBuilder(query)
|
||||
URL url = new URL(urlStr.toString())
|
||||
HttpURLConnection httpConnection = (HttpURLConnection) url.openConnection()
|
||||
httpConnection.setUseCaches(false)
|
||||
httpConnection.setDoOutput(true)
|
||||
httpConnection.setRequestMethod("HEAD")
|
||||
httpConnection.setConnectTimeout(5000)
|
||||
httpConnection.connect()
|
||||
|
||||
int responseCode = httpConnection.getResponseCode()
|
||||
if (responseCode == HttpURLConnection.HTTP_OK) {
|
||||
ssl_enabled = true
|
||||
} else {
|
||||
log.debug("HTTPERROR: Message=" + "BBB server responded with HTTP status code " + responseCode)
|
||||
}
|
||||
int responseCode = httpConnection.getResponseCode()
|
||||
if (responseCode == HttpURLConnection.HTTP_OK) {
|
||||
ssl_enabled = true
|
||||
} else {
|
||||
log.debug("HTTPERROR: Message=" + "BBB server responded with HTTP status code " + responseCode)
|
||||
}
|
||||
|
||||
} catch(IOException e) {
|
||||
log.debug("IOException: Message=" + e.getMessage())
|
||||
} catch(IllegalArgumentException e) {
|
||||
log.debug("IllegalArgumentException: Message=" + e.getMessage())
|
||||
} catch(Exception e) {
|
||||
log.debug("Exception: Message=" + e.getMessage())
|
||||
}
|
||||
}
|
||||
} catch(IOException e) {
|
||||
log.debug("IOException: Message=" + e.getMessage())
|
||||
} catch(IllegalArgumentException e) {
|
||||
log.debug("IllegalArgumentException: Message=" + e.getMessage())
|
||||
} catch(Exception e) {
|
||||
log.debug("Exception: Message=" + e.getMessage())
|
||||
}
|
||||
|
||||
return ssl_enabled
|
||||
}
|
||||
|
||||
def boolean hasRestrictedAccess() {
|
||||
return Boolean.parseBoolean(this.restrictedAccess);
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,12 @@
|
||||
# Handle request to bbb-web running within Tomcat. This is for
|
||||
# the BBB-API and Presentation.
|
||||
# Handle request to bbb-web running within Tomcat. This is for
|
||||
# the BBB-API and Presentation.
|
||||
location /lti {
|
||||
proxy_pass http://127.0.0.1:8080;
|
||||
proxy_redirect default;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header Scheme $scheme;
|
||||
proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto;
|
||||
|
||||
# Allow 30M uploaded presentation document.
|
||||
# Allow 30M uploaded presentation document.
|
||||
client_max_body_size 30m;
|
||||
client_body_buffer_size 128k;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user