Module: Lti2Commons::MessageSupport

Defined in:
lib/lti2_commons/lib/lti2_commons/message_support.rb

Overview

Module to support LTI 1 and 2 secure messaging. This messaging is documented in the LTI 2 Security Document

LTI 2 defines two types of LTI secure messaging:

1. LTI Messages
  This is the model used exclusively in LTI 1.
  It is also used in LTI 2 for user-submitted actions such as LtiLaunch and ToolDeployment.

  It works the following way:
    1. LTI parameters are signed by OAuth.
    2. The message is marshalled into an HTML Form with the params
       specified in form fields.
    3. The form is sent to the browser with a redirect.
    4. An attached Javascript script 'auto-submits' the form.

  This structure appears to the Tool Provider as a user submission with all user browser context
  intact.

2. LTI Services
  This is a standard REST web service with OAuth message security added.
  In LTI 2.0 Services are only defined for Tool Provider --> Tool Consumer services;
  such as, GetToolConsumerProfile, RegisterToolProxy, and LTI 2 Outcomes.
  LTI 2.x will add Tool Consumer --> Tool Provider services using the same machinery.

Constant Summary collapse

TIMEOUT =
300

Instance Method Summary collapse

Instance Method Details

#create_lti_message_body(launch_url, parameters, wire_log = nil, title = nil, is_open_in_external_window = false) ⇒ String

Convenience method signs and then invokes create_lti_message_from_signed_request

Returns:

  • (String)

    Post body ready for use



37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# File 'lib/lti2_commons/lib/lti2_commons/message_support.rb', line 37

def create_lti_message_body(launch_url, parameters, wire_log = nil, title = nil, is_open_in_external_window = false)
  result = create_message_header(launch_url, is_open_in_external_window)
  result += create_message_body(parameters)
  result += create_message_footer(is_open_in_external_window)

  if wire_log
    wire_log.timestamp
    wire_log.raw_log((title.nil?) ? 'LtiMessage' : "LtiMessage: #{title}")
    wire_log.raw_log "LaunchUrl: #{launch_url}"
    wire_log.raw_log result.strip
    wire_log.newline
    wire_log.flush
  end

  result
end

#create_lti_message_body_from_signed_request(signed_request, is_include_oauth_params = true, is_open_in_external_window = false) ⇒ String

Creates an LTI Message (POST body) ready for redirecting to the launch_url. Note that the is_include_oauth_params option specifies that ‘oauth_’ params are included in the body. This option should be false when sender is putting them in the HTTP Authorization header (now recommended).

Parameters:

  • params (Hash)

    Full set of params for message (including OAuth provided params)

Returns:

  • (String)

    Post body ready for use



62
63
64
65
66
67
68
# File 'lib/lti2_commons/lib/lti2_commons/message_support.rb', line 62

def create_lti_message_body_from_signed_request(signed_request, is_include_oauth_params = true,
                                                is_open_in_external_window = false)
  result = create_message_header(signed_request.uri, is_open_in_external_window)
  result += create_message_body(signed_request.parameters, is_include_oauth_params)
  result += create_message_footer(is_open_in_external_window)
  result
end

#invoke_service(request, wire_log = nil, title = nil, other_headers = {}) ⇒ Object

Invokes an LTI Service. This is fully-compliant REST request suitable for LTI server-to-server services.

Parameters:

  • request (Request)

    Signed Request encapsulates everything needed for service.



74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# File 'lib/lti2_commons/lib/lti2_commons/message_support.rb', line 74

def invoke_service(request, wire_log = nil, title = nil, other_headers = {})
  uri = request.uri.to_s
  # set_headers_proc = lambda { |http|
  # http.headers['Authorization'] = request.oauth_header
  # http.headers['Content-Type'] = request.content_type if request.content_type
  # http.headers['Accept'] = request.content_type if request.content_type
  # # http.headers['Content-Length'] = request.body.length if request.body
  # }
  method = request.method.downcase

  headers = {}
  headers['Authorization'] = request.oauth_header
  headers['Content-Type'] = request.content_type if request.content_type
  headers['Accept'] = request.accept if request.accept
  headers['Content-Length'] = request.body.length.to_s if request.body
  headers.merge!(other_headers)

  parameters = request.parameters
  output_parameters = {}
  parameters.each { |k, v| output_parameters[k] = v unless k =~ /^oauth_/ }

  (write_wirelog_header wire_log, title, request.method, uri, headers, parameters, request.body, output_parameters) if wire_log

  full_uri = uri
  full_uri += '?' unless uri.include? '?'
  full_uri += '&' unless full_uri =~ /[?&]$/
  output_parameters.each_pair do |key, _value|
    full_uri << '&' unless key == output_parameters.keys.first
    full_uri << "#{URI.encode(key.to_s)}=#{URI.encode(output_parameters[key] || '')}"
  end

  case method
  when 'get'
    response = HTTParty.get(full_uri, headers: headers, timeout: TIMEOUT)
  when 'post'
    response = HTTParty.post(full_uri, body: request.body, headers: headers, timeout: TIMEOUT)
  when 'put'
    response = HTTParty.put(full_uri, body: request.body, headers: headers, timeout: TIMEOUT)
  when 'delete'
    response = HTTParty.delete(full_uri, headers: headers, timeout: TIMEOUT)
  end

  wire_log.log_response(response, title) if wire_log

  response
end

#invoke_unsigned_service(uri, method, params = {}, headers = {}, data = nil, wire_log = nil, title = nil) ⇒ Object



121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
# File 'lib/lti2_commons/lib/lti2_commons/message_support.rb', line 121

def invoke_unsigned_service(uri, method, params = {}, headers = {},
                            data = nil, wire_log = nil, title = nil)
  full_uri = uri
  full_uri += '?' unless uri.include? '?'
  full_uri += '&' unless full_uri =~ /[?&]$/
  params.each_pair do |key, _value|
    full_uri << '&' unless key == params.keys.first
    full_uri << "#{URI.encode(key.to_s)}=#{URI.encode(params[key])}"
  end

  write_wirelog_header(wire_log, title, method, uri, headers, params, data, {}) if wire_log

  case method
  when 'get'
    response = HTTParty.get(full_uri, headers: headers, timeout: 120)
  when 'post'
    response = HTTParty.post(full_uri, body: data, headers: headers, timeout: 120)
  when 'put'
    response = HTTParty.put(full_uri, body: data, headers: headers, timeout: 120)
  when 'delete'
    response = HTTParty.delete(full_uri, headers: headers, timeout: 120)
  end

  wire_log.log_response(response, title) if wire_log

  response
end