atomic_lti 1.3.1 → 1.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +13 -1
- data/app/assets/builds/atomic_lti/init_app.js +46 -0
- data/app/assets/builds/atomic_lti/init_app.js.map +7 -0
- data/app/assets/stylesheets/atomic_lti/launch.css +95 -0
- data/app/javascript/atomic_lti/init_app.js +3 -0
- data/app/lib/atomic_lti/authorization.rb +16 -9
- data/app/lib/atomic_lti/definitions.rb +38 -6
- data/app/lib/atomic_lti/exceptions.rb +15 -11
- data/app/lib/atomic_lti/lti.rb +13 -5
- data/app/lib/atomic_lti/open_id.rb +25 -13
- data/app/lib/atomic_lti/params.rb +2 -2
- data/app/lib/atomic_lti/role_enforcement_mode.rb +8 -0
- data/app/lib/atomic_lti/services/base.rb +8 -7
- data/app/lib/atomic_lti/services/line_items.rb +15 -10
- data/app/lib/atomic_lti/services/names_and_roles.rb +11 -7
- data/app/lib/atomic_lti/services/results.rb +4 -0
- data/app/lib/atomic_lti/services/score.rb +8 -4
- data/app/models/atomic_lti/jwk.rb +10 -1
- data/app/models/atomic_lti/open_id_state.rb +1 -0
- data/app/views/atomic_lti/shared/error.html.erb +17 -0
- data/app/views/atomic_lti/shared/init.html.erb +26 -0
- data/app/views/atomic_lti/shared/redirect.html.erb +21 -8
- data/db/migrate/20230726040941_add_state_to_open_id_state.rb +6 -0
- data/db/seeds.rb +8 -8
- data/lib/atomic_lti/error_handling_middleware.rb +19 -11
- data/lib/atomic_lti/open_id_middleware.rb +141 -62
- data/lib/atomic_lti/version.rb +1 -1
- data/lib/atomic_lti.rb +28 -1
- metadata +11 -3
@@ -1,15 +1,28 @@
|
|
1
1
|
<!DOCTYPE html>
|
2
2
|
<html lang="en">
|
3
3
|
<head>
|
4
|
-
<
|
5
|
-
|
6
|
-
</script>
|
4
|
+
<link href="https://fonts.googleapis.com/icon?family=Material+Icons+Outlined" rel="stylesheet">
|
5
|
+
<%= stylesheet_link_tag "atomic_lti/launch" %>
|
7
6
|
</head>
|
8
7
|
<body>
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
8
|
+
<noscript>
|
9
|
+
<div class="u-flex aj-centered-message">
|
10
|
+
<i class="material-icons-outlined aj-icon" aria-hidden="true">warning</i>
|
11
|
+
<p class="aj-text">
|
12
|
+
You must have javascript enabled to use this application.
|
13
|
+
</p>
|
14
|
+
</div>
|
15
|
+
</noscript>
|
16
|
+
<form action="<%= @launch_url -%>" method="POST">
|
17
|
+
<% @launch_params.each do |name, value| -%>
|
18
|
+
<%= hidden_field_tag(name, value) %>
|
19
|
+
<% end -%>
|
20
|
+
</form>
|
21
|
+
</div>
|
22
|
+
<script>
|
23
|
+
window.addEventListener("load", () => {
|
24
|
+
document.forms[0].submit();
|
25
|
+
});
|
26
|
+
</script>
|
14
27
|
</body>
|
15
28
|
</html>
|
data/db/seeds.rb
CHANGED
@@ -2,16 +2,16 @@
|
|
2
2
|
AtomicLti::Jwk.find_or_create_by(domain: nil)
|
3
3
|
|
4
4
|
# Add some platforms
|
5
|
-
AtomicLti::Platform.create_with(
|
6
|
-
jwks_url:
|
7
|
-
token_url:
|
8
|
-
oidc_url:
|
5
|
+
AtomicLti::Platform.create_with(
|
6
|
+
jwks_url: AtomicLti::Definitions::CANVAS_PUBLIC_LTI_KEYS_URL,
|
7
|
+
token_url: AtomicLti::Definitions::CANVAS_AUTH_TOKEN_URL,
|
8
|
+
oidc_url: AtomicLti::Definitions::CANVAS_OIDC_URL,
|
9
9
|
).find_or_create_by(iss: "https://canvas.instructure.com")
|
10
10
|
|
11
11
|
AtomicLti::Platform.create_with(
|
12
|
-
jwks_url:
|
13
|
-
token_url:
|
14
|
-
oidc_url:
|
12
|
+
jwks_url: AtomicLti::Definitions::CANVAS_BETA_PUBLIC_LTI_KEYS_URL,
|
13
|
+
token_url: AtomicLti::Definitions::CANVAS_BETA_AUTH_TOKEN_URL,
|
14
|
+
oidc_url: AtomicLti::Definitions::CANVAS_BETA_OIDC_URL,
|
15
15
|
).find_or_create_by(iss: "https://canvas-beta.instructure.com")
|
16
16
|
|
17
17
|
|
@@ -26,4 +26,4 @@ AtomicTenant::PinnedPlatformGuid.create(iss: "https://canvas.instructure.com", p
|
|
26
26
|
# deployment_id: "21089:1f5e1ee417cb2b17f86a1232122452ab3f6188f7",
|
27
27
|
# application_instance_id: 5,
|
28
28
|
# created_at: Tue, 16 Aug 2022 16:05:20.848365000 UTC +00:00,
|
29
|
-
# updated_at: Tue, 16 Aug 2022 16:05:20.848365000 UTC +00:00>
|
29
|
+
# updated_at: Tue, 16 Aug 2022 16:05:20.848365000 UTC +00:00>
|
@@ -4,7 +4,7 @@ module AtomicLti
|
|
4
4
|
@app = app
|
5
5
|
end
|
6
6
|
|
7
|
-
def render_error(
|
7
|
+
def render_error(status, message)
|
8
8
|
format = "text/plain"
|
9
9
|
body = message
|
10
10
|
|
@@ -12,22 +12,30 @@ module AtomicLti
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def render(status, body, format)
|
15
|
-
[
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
15
|
+
[
|
16
|
+
status,
|
17
|
+
{
|
18
|
+
"Content-Type" => "#{format}; charset=\"UTF-8\"",
|
19
|
+
"Content-Length" => body.bytesize.to_s,
|
20
|
+
},
|
21
|
+
[body],
|
22
|
+
]
|
21
23
|
end
|
22
24
|
|
23
25
|
def call(env)
|
24
26
|
@app.call(env)
|
25
|
-
|
27
|
+
rescue JWT::ExpiredSignature
|
28
|
+
render_error(401, "The launch has expired. Please launch the application again.")
|
29
|
+
rescue JWT::DecodeError
|
30
|
+
render_error(401, "The launch token is invalid.")
|
31
|
+
rescue AtomicLti::Exceptions::NoLTIToken
|
32
|
+
render_error(401, "Invalid launch. Please launch the application again.")
|
33
|
+
rescue AtomicLti::Exceptions::AtomicLtiAuthException => e
|
34
|
+
render_error(401, "Invalid LTI launch. Please launch the application again. #{e.message}")
|
26
35
|
rescue AtomicLti::Exceptions::AtomicLtiNotFoundException => e
|
27
|
-
render_error(
|
28
|
-
|
36
|
+
render_error(404, e.message)
|
29
37
|
rescue AtomicLti::Exceptions::AtomicLtiException => e
|
30
|
-
render_error(
|
38
|
+
render_error(500, "Invalid LTI launch. Please launch the application again. #{e.message}")
|
31
39
|
end
|
32
40
|
end
|
33
41
|
end
|
@@ -1,4 +1,8 @@
|
|
1
1
|
module AtomicLti
|
2
|
+
# This is the same prefix used in the npm package. There's not a great way to share constants between ruby and npm.
|
3
|
+
# Don't change it unless you change it in the Javascript as well.
|
4
|
+
OPEN_ID_COOKIE_PREFIX = "open_id_".freeze
|
5
|
+
|
2
6
|
class OpenIdMiddleware
|
3
7
|
def initialize(app)
|
4
8
|
@app = app
|
@@ -17,26 +21,86 @@ module AtomicLti
|
|
17
21
|
end
|
18
22
|
|
19
23
|
def handle_init(request)
|
20
|
-
|
24
|
+
platform = AtomicLti::Platform.find_by(iss: request.params["iss"])
|
25
|
+
if !platform
|
26
|
+
raise AtomicLti::Exceptions::NoLTIPlatform.new(iss: request.params["iss"])
|
27
|
+
end
|
28
|
+
|
29
|
+
nonce, state = AtomicLti::OpenId.generate_state
|
30
|
+
|
31
|
+
headers = { "Content-Type" => "text/html" }
|
32
|
+
Rack::Utils.set_cookie_header!(
|
33
|
+
headers, "#{OPEN_ID_COOKIE_PREFIX}storage",
|
34
|
+
{ value: "1", path: "/", max_age: 365.days, http_only: false, secure: true, same_site: "None" }
|
35
|
+
)
|
36
|
+
Rack::Utils.set_cookie_header!(
|
37
|
+
headers, "#{OPEN_ID_COOKIE_PREFIX}#{state}",
|
38
|
+
{ value: 1, path: "/", max_age: 1.minute, http_only: false, secure: true, same_site: "None" }
|
39
|
+
)
|
21
40
|
|
22
41
|
redirect_uri = [request.base_url, AtomicLti.oidc_redirect_path].join
|
42
|
+
response_url = build_oidc_response(request, state, nonce, redirect_uri)
|
43
|
+
|
44
|
+
if request.cookies.present? || !AtomicLti.enforce_csrf_protection
|
45
|
+
# we know cookies will work, so redirect
|
46
|
+
headers["Location"] = response_url
|
23
47
|
|
24
|
-
|
25
|
-
|
48
|
+
[302, headers, ["Found"]]
|
49
|
+
else
|
50
|
+
# cookies might not work, so render our javascript form
|
51
|
+
if request.params["lti_storage_target"].present? && AtomicLti.use_post_message_storage
|
52
|
+
lti_storage_params = build_lti_storage_params(request, platform)
|
53
|
+
end
|
26
54
|
|
27
|
-
|
28
|
-
|
29
|
-
|
55
|
+
html = ApplicationController.renderer.render(
|
56
|
+
:html,
|
57
|
+
layout: false,
|
58
|
+
template: "atomic_lti/shared/init",
|
59
|
+
assigns: {
|
60
|
+
settings: {
|
61
|
+
state: state,
|
62
|
+
responseUrl: response_url,
|
63
|
+
ltiStorageParams: lti_storage_params,
|
64
|
+
relaunchInitUrl: relaunch_init_url(request),
|
65
|
+
privacyPolicyUrl: AtomicLti.privacy_policy_url,
|
66
|
+
privacyPolicyMessage: AtomicLti.privacy_policy_message,
|
67
|
+
openIdCookiePrefix: OPEN_ID_COOKIE_PREFIX,
|
68
|
+
},
|
69
|
+
},
|
70
|
+
)
|
71
|
+
|
72
|
+
[200, headers, [html]]
|
73
|
+
end
|
30
74
|
end
|
31
75
|
|
32
|
-
def
|
76
|
+
def validate_launch(request, validate_target_link_url)
|
77
|
+
# Validate and decode id_token
|
33
78
|
raise AtomicLti::Exceptions::NoLTIToken if request.params["id_token"].blank?
|
34
79
|
|
35
|
-
|
36
|
-
|
37
|
-
|
80
|
+
id_token_decoded = AtomicLti::Authorization.validate_token(request.params["id_token"])
|
81
|
+
|
82
|
+
raise AtomicLti::Exceptions::InvalidLTIToken.new if id_token_decoded.nil?
|
83
|
+
|
84
|
+
# Validate id token contents
|
85
|
+
AtomicLti::Lti.validate!(id_token_decoded, request.url, validate_target_link_url)
|
86
|
+
|
87
|
+
# Check for the state cookie
|
88
|
+
state_verified = false
|
89
|
+
state = request.params["state"]
|
90
|
+
if request.cookies["open_id_#{state}"]
|
91
|
+
state_verified = true
|
92
|
+
end
|
38
93
|
|
39
|
-
|
94
|
+
# Validate the state and nonce
|
95
|
+
if !AtomicLti::OpenId.validate_state(id_token_decoded["nonce"], state)
|
96
|
+
raise AtomicLti::Exceptions::OpenIDStateError.new("Invalid OIDC state.")
|
97
|
+
end
|
98
|
+
|
99
|
+
[id_token_decoded, state, state_verified]
|
100
|
+
end
|
101
|
+
|
102
|
+
def handle_redirect(request)
|
103
|
+
id_token_decoded, _state, _state_verified = validate_launch(request, false)
|
40
104
|
|
41
105
|
uri = URI(request.url)
|
42
106
|
# Technically the target_link_uri is not required and the certification suite
|
@@ -44,25 +108,26 @@ module AtomicLti
|
|
44
108
|
# but at least for the certification suite we have to have a backup default
|
45
109
|
# value that can be set in the configuration of Atomic LTI using
|
46
110
|
# the default_deep_link_path
|
47
|
-
target_link_uri =
|
111
|
+
target_link_uri = id_token_decoded[AtomicLti::Definitions::TARGET_LINK_URI_CLAIM] ||
|
48
112
|
File.join("#{uri.scheme}://#{uri.host}", AtomicLti.default_deep_link_path)
|
49
113
|
|
50
|
-
redirect_params = {
|
51
|
-
state: request.params["state"],
|
52
|
-
id_token: request.params["id_token"],
|
53
|
-
}
|
54
114
|
html = ApplicationController.renderer.render(
|
55
115
|
:html,
|
56
116
|
layout: false,
|
57
117
|
template: "atomic_lti/shared/redirect",
|
58
|
-
assigns: {
|
118
|
+
assigns: {
|
119
|
+
launch_params: request.params,
|
120
|
+
launch_url: target_link_uri,
|
121
|
+
},
|
59
122
|
)
|
60
123
|
|
61
124
|
[200, { "Content-Type" => "text/html" }, [html]]
|
62
125
|
end
|
63
126
|
|
64
127
|
def matches_redirect?(request)
|
65
|
-
|
128
|
+
if AtomicLti.oidc_redirect_path.blank?
|
129
|
+
raise AtomicLti::Exceptions::ConfigurationError.new("AtomicLti.oidc_redirect_path is not configured")
|
130
|
+
end
|
66
131
|
|
67
132
|
redirect_uri = URI.parse(AtomicLti.oidc_redirect_path)
|
68
133
|
redirect_path_params = if redirect_uri.query
|
@@ -87,35 +152,42 @@ module AtomicLti
|
|
87
152
|
end
|
88
153
|
|
89
154
|
def handle_lti_launch(env, request)
|
90
|
-
|
91
|
-
state = request.params["state"]
|
92
|
-
url = request.url
|
155
|
+
id_token_decoded, state, state_verified = validate_launch(request, true)
|
93
156
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
157
|
+
id_token = request.params["id_token"]
|
158
|
+
update_install(id_token: id_token_decoded)
|
159
|
+
update_platform_instance(id_token: id_token_decoded)
|
160
|
+
update_deployment(id_token: id_token_decoded)
|
161
|
+
update_lti_context(id_token: id_token_decoded)
|
162
|
+
|
163
|
+
errors = id_token_decoded.dig(AtomicLti::Definitions::TOOL_PLATFORM_CLAIM, "errors")
|
164
|
+
if errors.present? && !errors["errors"].empty?
|
165
|
+
Rails.logger.error("Detected errors in lti launch: #{errors}, id_token: #{id_token}")
|
166
|
+
end
|
102
167
|
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
168
|
+
env["atomic.validated.decoded_id_token"] = id_token_decoded
|
169
|
+
env["atomic.validated.id_token"] = id_token
|
170
|
+
|
171
|
+
platform = AtomicLti::Platform.find_by!(iss: id_token_decoded["iss"])
|
172
|
+
if request.params["lti_storage_target"].present? && AtomicLti.use_post_message_storage
|
173
|
+
lti_storage_params = build_lti_storage_params(request, platform)
|
174
|
+
# Add the values needed to do client side validate to the environment
|
175
|
+
env["atomic.validated.state_validation"] = {
|
176
|
+
state: state,
|
177
|
+
lti_storage_params: lti_storage_params,
|
178
|
+
verified_by_cookie: state_verified,
|
179
|
+
}
|
180
|
+
end
|
107
181
|
|
108
|
-
|
109
|
-
env["atomic.validated.id_token"] = id_token
|
182
|
+
@app.call(env)
|
110
183
|
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
end
|
184
|
+
# Delete the state cookie
|
185
|
+
status, headers, body = @app.call(env)
|
186
|
+
# Rack::Utils.delete_cookie_header(headers, "#{OPEN_ID_COOKIE_PREFIX}#{state}")
|
187
|
+
[status, headers, body]
|
116
188
|
end
|
117
189
|
|
118
|
-
def error!(body = "Error", status = 500, headers = {"Content-Type" => "text/html"})
|
190
|
+
def error!(body = "Error", status = 500, headers = { "Content-Type" => "text/html" })
|
119
191
|
[status, headers, [body]]
|
120
192
|
end
|
121
193
|
|
@@ -134,6 +206,19 @@ module AtomicLti
|
|
134
206
|
|
135
207
|
protected
|
136
208
|
|
209
|
+
def render_error(status, message)
|
210
|
+
html = ApplicationController.renderer.render(
|
211
|
+
:html,
|
212
|
+
layout: false,
|
213
|
+
template: "atomic_lti/shared/error",
|
214
|
+
assigns: {
|
215
|
+
message: message || "There was an error during the launch. Please try again.",
|
216
|
+
},
|
217
|
+
)
|
218
|
+
|
219
|
+
[status || 404, { "Content-Type" => "text/html" }, [html]]
|
220
|
+
end
|
221
|
+
|
137
222
|
def update_platform_instance(id_token:)
|
138
223
|
if id_token[AtomicLti::Definitions::TOOL_PLATFORM_CLAIM].present? &&
|
139
224
|
id_token.dig(AtomicLti::Definitions::TOOL_PLATFORM_CLAIM, "guid").present?
|
@@ -222,32 +307,18 @@ module AtomicLti
|
|
222
307
|
)
|
223
308
|
end
|
224
309
|
|
225
|
-
def
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
token = false
|
232
|
-
|
233
|
-
begin
|
234
|
-
token = AtomicLti::Authorization.validate_token(id_token)
|
235
|
-
rescue JWT::DecodeError => e
|
236
|
-
Rails.logger.error("Unable to decode jwt: #{e}, #{e.backtrace}")
|
237
|
-
return false
|
238
|
-
end
|
239
|
-
|
240
|
-
return false if token.nil?
|
241
|
-
|
242
|
-
AtomicLti::Lti.validate!(token, url, true)
|
243
|
-
|
244
|
-
token
|
310
|
+
def relaunch_init_url(request)
|
311
|
+
uri = URI.parse(request.url)
|
312
|
+
uri.fragment = uri.query = nil
|
313
|
+
params = request.params
|
314
|
+
params.delete("lti_storage_target")
|
315
|
+
[uri.to_s, "?", params.to_query].join
|
245
316
|
end
|
246
317
|
|
247
318
|
def build_oidc_response(request, state, nonce, redirect_uri)
|
248
319
|
platform = AtomicLti::Platform.find_by(iss: request.params["iss"])
|
249
320
|
if !platform
|
250
|
-
raise AtomicLti::Exceptions::NoLTIPlatform.new(iss: request.params[
|
321
|
+
raise AtomicLti::Exceptions::NoLTIPlatform.new("No platform was found for iss: #{request.params['iss']}")
|
251
322
|
end
|
252
323
|
|
253
324
|
uri = URI.parse(platform.oidc_url)
|
@@ -268,5 +339,13 @@ module AtomicLti
|
|
268
339
|
|
269
340
|
[uri.to_s, "?", auth_params.to_query].join
|
270
341
|
end
|
342
|
+
|
343
|
+
def build_lti_storage_params(request, platform)
|
344
|
+
{
|
345
|
+
target: request.params["lti_storage_target"],
|
346
|
+
originSupportBroken: !AtomicLti.set_post_message_origin,
|
347
|
+
platformOIDCUrl: platform.oidc_url,
|
348
|
+
}
|
349
|
+
end
|
271
350
|
end
|
272
351
|
end
|
data/lib/atomic_lti/version.rb
CHANGED
data/lib/atomic_lti.rb
CHANGED
@@ -4,6 +4,7 @@ require "atomic_lti/open_id_middleware"
|
|
4
4
|
require "atomic_lti/error_handling_middleware"
|
5
5
|
require_relative "../app/lib/atomic_lti/definitions"
|
6
6
|
require_relative "../app/lib/atomic_lti/exceptions"
|
7
|
+
require_relative "../app/lib/atomic_lti/role_enforcement_mode"
|
7
8
|
module AtomicLti
|
8
9
|
|
9
10
|
# Set this to true to scope context_id's to the ISS rather than
|
@@ -18,7 +19,33 @@ module AtomicLti
|
|
18
19
|
mattr_accessor :target_link_path_prefixes
|
19
20
|
mattr_accessor :default_deep_link_path
|
20
21
|
mattr_accessor :jwt_secret
|
21
|
-
mattr_accessor :scopes
|
22
|
+
mattr_accessor :scopes, default: AtomicLti::Definitions.scopes.join(" ")
|
23
|
+
|
24
|
+
# Set to true to enforce CSRF protection, either via cookies or postMessage
|
25
|
+
mattr_accessor :enforce_csrf_protection, default: true
|
26
|
+
|
27
|
+
# Set to true to use LTI postMessage storage for csrf token storage
|
28
|
+
# with this enabled we can operate without cookies
|
29
|
+
mattr_accessor :use_post_message_storage, default: true
|
30
|
+
|
31
|
+
# Set to true to set the targetOrigin on postMessage calls. The LTI spec
|
32
|
+
# requires this, but Canvas doesn't currently support it.
|
33
|
+
mattr_accessor :set_post_message_origin, default: false
|
34
|
+
|
35
|
+
mattr_accessor :privacy_policy_url, default: "#"
|
36
|
+
mattr_accessor :privacy_policy_message, default: nil
|
37
|
+
|
38
|
+
# https://www.imsglobal.org/spec/lti/v1p3#anonymous-launch-case
|
39
|
+
# 'anonymous' here means that the launch does not include a 'sub' field. In
|
40
|
+
# Canvas, this means the user is not logged in at all. If you enable this
|
41
|
+
# option, you will likely have to adjust application code to accommodate
|
42
|
+
mattr_accessor :allow_anonymous_user, default: false
|
43
|
+
|
44
|
+
# https://www.imsglobal.org/spec/lti/v1p3#role-vocabularies
|
45
|
+
# Determines how strictly to enforce the role vocabulary. The options are:
|
46
|
+
# - "DEFAULT" which means that unknown roles are allowed to be the only roles in the token.
|
47
|
+
# - "STRICT" which means that unknown roles are not allowed to be the only roles in the token.
|
48
|
+
mattr_accessor :role_enforcement_mode, default: AtomicLti::RoleEnforcementMode::DEFAULT
|
22
49
|
|
23
50
|
def self.get_deployments(iss:, deployment_ids:)
|
24
51
|
AtomicLti::Deployment.where(iss: iss, deployment_id: deployment_ids)
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: atomic_lti
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.5.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Matt Petro
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2023-
|
13
|
+
date: 2023-08-17 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: pg
|
@@ -53,11 +53,15 @@ files:
|
|
53
53
|
- MIT-LICENSE
|
54
54
|
- README.md
|
55
55
|
- Rakefile
|
56
|
+
- app/assets/builds/atomic_lti/init_app.js
|
57
|
+
- app/assets/builds/atomic_lti/init_app.js.map
|
56
58
|
- app/assets/config/atomic_lti_manifest.js
|
57
59
|
- app/assets/stylesheets/atomic_lti/application.css
|
58
60
|
- app/assets/stylesheets/atomic_lti/jwks.css
|
61
|
+
- app/assets/stylesheets/atomic_lti/launch.css
|
59
62
|
- app/controllers/atomic_lti/jwks_controller.rb
|
60
63
|
- app/helpers/atomic_lti/launch_helper.rb
|
64
|
+
- app/javascript/atomic_lti/init_app.js
|
61
65
|
- app/jobs/atomic_lti/application_job.rb
|
62
66
|
- app/lib/atomic_lti/auth_token.rb
|
63
67
|
- app/lib/atomic_lti/authorization.rb
|
@@ -68,6 +72,7 @@ files:
|
|
68
72
|
- app/lib/atomic_lti/lti.rb
|
69
73
|
- app/lib/atomic_lti/open_id.rb
|
70
74
|
- app/lib/atomic_lti/params.rb
|
75
|
+
- app/lib/atomic_lti/role_enforcement_mode.rb
|
71
76
|
- app/lib/atomic_lti/services/base.rb
|
72
77
|
- app/lib/atomic_lti/services/line_items.rb
|
73
78
|
- app/lib/atomic_lti/services/names_and_roles.rb
|
@@ -85,6 +90,8 @@ files:
|
|
85
90
|
- app/models/atomic_lti/platform.rb
|
86
91
|
- app/models/atomic_lti/platform_instance.rb
|
87
92
|
- app/views/atomic_lti/launches/index.html.erb
|
93
|
+
- app/views/atomic_lti/shared/error.html.erb
|
94
|
+
- app/views/atomic_lti/shared/init.html.erb
|
88
95
|
- app/views/atomic_lti/shared/redirect.html.erb
|
89
96
|
- app/views/layouts/atomic_lti/application.html.erb
|
90
97
|
- config/routes.rb
|
@@ -96,6 +103,7 @@ files:
|
|
96
103
|
- db/migrate/20220428175423_create_atomic_lti_oauth_states.rb
|
97
104
|
- db/migrate/20220503003528_create_atomic_lti_jwks.rb
|
98
105
|
- db/migrate/20221010140920_create_open_id_state.rb
|
106
|
+
- db/migrate/20230726040941_add_state_to_open_id_state.rb
|
99
107
|
- db/seeds.rb
|
100
108
|
- lib/atomic_lti.rb
|
101
109
|
- lib/atomic_lti/engine.rb
|
@@ -124,7 +132,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
124
132
|
- !ruby/object:Gem::Version
|
125
133
|
version: '0'
|
126
134
|
requirements: []
|
127
|
-
rubygems_version: 3.
|
135
|
+
rubygems_version: 3.4.15
|
128
136
|
signing_key:
|
129
137
|
specification_version: 4
|
130
138
|
summary: AtomicLti implements the LTI Advantage specification.
|