atomic_lti 1.3.1 → 1.5.1
Sign up to get free protection for your applications and to get access to all the features.
- 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.
|