atomic_admin 1.1.0 → 2.0.0.beta.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/app/controllers/atomic_admin/api/admin/v1/application_instances_controller.rb +3 -0
- data/app/controllers/atomic_admin/api/admin/v1/applications_controller.rb +3 -0
- data/app/controllers/atomic_admin/api/admin/v1/lti_installs_controller.rb +3 -0
- data/app/controllers/atomic_admin/api/admin/v1/lti_platforms_controller.rb +3 -0
- data/app/controllers/atomic_admin/api/admin/v1/sites_controller.rb +3 -0
- data/app/controllers/atomic_admin/api/admin/v1/tenant_client_id_strategies_controller.rb +3 -0
- data/app/controllers/atomic_admin/api/admin/v1/tenant_deployments_controller.rb +3 -0
- data/app/controllers/atomic_admin/api/admin/v1/tenant_platform_guid_strategies_controller.rb +3 -0
- data/app/controllers/atomic_admin/v1/admin_controller.rb +49 -0
- data/app/controllers/atomic_admin/v1/application_instances_controller.rb +127 -0
- data/app/controllers/atomic_admin/v1/applications_controller.rb +49 -0
- data/app/controllers/atomic_admin/{atomic_lti_install_controller.rb → v1/lti_installs_controller.rb} +18 -12
- data/app/controllers/atomic_admin/v1/lti_platforms_controller.rb +50 -0
- data/app/controllers/atomic_admin/v1/sites_controller.rb +46 -0
- data/app/controllers/atomic_admin/v1/tenant_client_id_strategies_controller.rb +48 -0
- data/app/controllers/atomic_admin/v1/tenant_deployments_controller.rb +47 -0
- data/app/controllers/atomic_admin/v1/tenant_platform_guid_strategies_controller.rb +63 -0
- data/app/controllers/concerns/filtering.rb +54 -0
- data/app/controllers/concerns/require_jwt_token.rb +77 -0
- data/config/routes.rb +25 -9
- data/lib/atomic_admin/interaction.rb +48 -0
- data/lib/atomic_admin/jwt_token/jwks_decoder.rb +41 -0
- data/lib/atomic_admin/jwt_token/secret_decoder.rb +29 -0
- data/lib/atomic_admin/jwt_token.rb +5 -65
- data/lib/atomic_admin/schema/application_instance_configuration_schema.rb +61 -0
- data/lib/atomic_admin/schema/application_instance_create_schema.rb +77 -0
- data/lib/atomic_admin/schema/application_instance_general_settings_schema.rb +156 -0
- data/lib/atomic_admin/schema/application_instance_license_details_schema.rb +99 -0
- data/lib/atomic_admin/schema/application_instance_schema.rb +18 -0
- data/lib/atomic_admin/schema/application_instance_trial_details_schema.rb +67 -0
- data/lib/atomic_admin/schema/application_instance_xml_config_schema.rb +66 -0
- data/lib/atomic_admin/schema/atomic_application_update_schema.rb +57 -0
- data/lib/atomic_admin/schema.rb +13 -0
- data/lib/atomic_admin/version.rb +1 -1
- data/lib/atomic_admin.rb +10 -6
- metadata +35 -10
- data/app/controllers/atomic_admin/application_controller.rb +0 -10
- data/app/controllers/atomic_admin/atomic_lti_platform_controller.rb +0 -43
- data/app/controllers/atomic_admin/atomic_tenant_client_id_strategy_controller.rb +0 -59
- data/app/controllers/atomic_admin/atomic_tenant_deployment_controller.rb +0 -79
- data/app/controllers/atomic_admin/atomic_tenant_platform_guid_strategy_controller.rb +0 -58
@@ -0,0 +1,77 @@
|
|
1
|
+
module RequireJwtToken
|
2
|
+
extend ActiveSupport::Concern
|
3
|
+
|
4
|
+
included do
|
5
|
+
attr_accessor :auth_source
|
6
|
+
|
7
|
+
before_action :validate_admin_token
|
8
|
+
before_action :validate_internal_token
|
9
|
+
end
|
10
|
+
|
11
|
+
def is_atomic_admin?
|
12
|
+
self.auth_source == :atomic_admin
|
13
|
+
end
|
14
|
+
|
15
|
+
def is_internal?
|
16
|
+
self.auth_source == :internal
|
17
|
+
end
|
18
|
+
|
19
|
+
protected
|
20
|
+
|
21
|
+
def validate_admin_token
|
22
|
+
encoded_token = get_encoded_token(request)
|
23
|
+
decoder = AtomicAdmin::JwtToken::JwksDecoder.new(AtomicAdmin.admin_jwks_url)
|
24
|
+
token = decoder.decode(encoded_token)&.first
|
25
|
+
validate_claims!(token)
|
26
|
+
self.auth_source = :atomic_admin
|
27
|
+
|
28
|
+
token
|
29
|
+
rescue Exception => e
|
30
|
+
# Capture all exceptions to let the internal token validation handle it
|
31
|
+
Rails.logger.error "Admin JWT Error occured #{e.inspect}"
|
32
|
+
nil
|
33
|
+
end
|
34
|
+
|
35
|
+
def validate_internal_token
|
36
|
+
return if is_atomic_admin?
|
37
|
+
|
38
|
+
encoded_token = get_encoded_token(request)
|
39
|
+
decoder = AtomicAdmin::JwtToken::SecretDecoder.new(AtomicAdmin.internal_secret)
|
40
|
+
token = decoder.decode!(encoded_token)
|
41
|
+
validate_claims!(token)
|
42
|
+
|
43
|
+
current_application_instance_id = request.env['atomic.validated.application_instance_id']
|
44
|
+
if current_application_instance_id && current_application_instance_id != token["application_instance_id"]
|
45
|
+
raise AtomicAdmin::JwtToken::InvalidTokenError, "Invalid application instance id"
|
46
|
+
end
|
47
|
+
|
48
|
+
@user_tenant = token["user_tenant"] if token["user_tenant"].present?
|
49
|
+
@user = User.find(token["user_id"])
|
50
|
+
|
51
|
+
sign_in(@user, event: :authentication, store: false)
|
52
|
+
self.auth_source = :internal
|
53
|
+
rescue JWT::DecodeError, AtomicAdmin::JwtToken::InvalidTokenError => e
|
54
|
+
Rails.logger.error "Internal JWT Error occured #{e.inspect}"
|
55
|
+
render json: { error: "Unauthorized: Invalid token." }, status: :unauthorized
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def get_encoded_token(req)
|
61
|
+
return req.params[:jwt] if req.params[:jwt]
|
62
|
+
|
63
|
+
header = req.headers["Authorization"] || req.headers[:authorization]
|
64
|
+
raise AtomicAdmin::JwtToken::MissingTokenError, "No authorization header found" if header.nil?
|
65
|
+
|
66
|
+
token = header.split(" ").last
|
67
|
+
raise AtomicAdmin::JwtToken::MissingTokenError, "Invalid authorization header string" if token.nil?
|
68
|
+
|
69
|
+
token
|
70
|
+
end
|
71
|
+
|
72
|
+
def validate_claims!(token)
|
73
|
+
if AtomicAdmin.audience != token["aud"]
|
74
|
+
raise AtomicAdmin::JwtToken::InvalidTokenError, "Expected audience to be #{AtomicAdmin.audience} but was #{token["aud"]}"
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
data/config/routes.rb
CHANGED
@@ -1,13 +1,29 @@
|
|
1
1
|
AtomicAdmin::Engine.routes.draw do
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
2
|
+
namespace :api do
|
3
|
+
namespace :admin do
|
4
|
+
namespace :v1 do
|
5
|
+
resources :lti_platforms
|
6
|
+
resources :lti_installs
|
7
|
+
resources :tenant_deployments
|
8
|
+
resources :sites
|
7
9
|
|
8
|
-
|
9
|
-
|
10
|
-
|
10
|
+
resources :applications do
|
11
|
+
member do
|
12
|
+
get :interactions
|
13
|
+
end
|
11
14
|
|
12
|
-
|
15
|
+
resources :application_instances do
|
16
|
+
member do
|
17
|
+
get :interactions
|
18
|
+
get :stats
|
19
|
+
end
|
20
|
+
|
21
|
+
resources :tenant_client_id_strategies
|
22
|
+
resources :tenant_platform_guid_strategies
|
23
|
+
resources :tenant_deployments
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
13
29
|
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module AtomicAdmin::Interaction
|
2
|
+
class Manager
|
3
|
+
def initialize
|
4
|
+
@interactions = {}
|
5
|
+
@curr_index = 0
|
6
|
+
end
|
7
|
+
|
8
|
+
def add(key, **kwargs)
|
9
|
+
@interactions[key] = {
|
10
|
+
**kwargs,
|
11
|
+
order: @curr_index,
|
12
|
+
}
|
13
|
+
@curr_index += 1
|
14
|
+
end
|
15
|
+
|
16
|
+
def get(key)
|
17
|
+
@interactions[key]
|
18
|
+
end
|
19
|
+
|
20
|
+
def tap
|
21
|
+
yield self
|
22
|
+
self
|
23
|
+
end
|
24
|
+
|
25
|
+
def resolve(**kwargs)
|
26
|
+
sorted = @interactions.sort_by { |key, interaction| interaction[:order] }
|
27
|
+
sorted.map do |key, interaction|
|
28
|
+
type = interaction[:type]
|
29
|
+
hash = {
|
30
|
+
key: key,
|
31
|
+
type: type,
|
32
|
+
title: interaction[:title],
|
33
|
+
icon: interaction[:icon],
|
34
|
+
}
|
35
|
+
|
36
|
+
case type
|
37
|
+
when :jsonform
|
38
|
+
schema_factory = interaction[:schema]
|
39
|
+
schema = schema_factory.new(**kwargs)
|
40
|
+
hash[:schema] = schema.schema
|
41
|
+
hash[:uischema] = schema.uischema
|
42
|
+
end
|
43
|
+
|
44
|
+
hash
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module AtomicAdmin::JwtToken
|
2
|
+
# Decodes a JWT token using the JWKS endpoint
|
3
|
+
# This is used for decoding JWT tokens issued by the new
|
4
|
+
# admin app
|
5
|
+
class JwksDecoder
|
6
|
+
ALGORITHMS = ["RS256"].freeze
|
7
|
+
|
8
|
+
def initialize(jwks_url, algorithms = ALGORITHMS)
|
9
|
+
@jwks_url = jwks_url
|
10
|
+
@algorithms = algorithms
|
11
|
+
end
|
12
|
+
|
13
|
+
def decode(token, validate = true)
|
14
|
+
load_admin_jwks = ->(options) do
|
15
|
+
Rails.cache.delete("atomic_admin_jwks") if options[:kid_not_found]
|
16
|
+
|
17
|
+
# NOTE: the cached keys only expire when we recieve a kid_not_found error
|
18
|
+
keys = Rails.cache.fetch("atomic_admin_jwks") do
|
19
|
+
HTTParty.get(@jwks_url).parsed_response
|
20
|
+
end
|
21
|
+
|
22
|
+
JWT::JWK::Set.new(keys).select { |k| k[:use] == "sig" }
|
23
|
+
end
|
24
|
+
|
25
|
+
JWT.decode(
|
26
|
+
token,
|
27
|
+
nil,
|
28
|
+
validate,
|
29
|
+
{ algorithms: @algorithms, jwks: load_admin_jwks },
|
30
|
+
)
|
31
|
+
end
|
32
|
+
|
33
|
+
def decode!(token)
|
34
|
+
token = decode(token)
|
35
|
+
raise AtomicAdmin::JwtToken::InvalidTokenError, "Unable to decode jwt token" if token.blank?
|
36
|
+
raise AtomicAdmin::JwtToken::InvalidTokenError, "Invalid token payload" if token.empty?
|
37
|
+
|
38
|
+
token[0]
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module AtomicAdmin::JwtToken
|
2
|
+
# Decodes a JWT token using a known secret. This is used for decoding
|
3
|
+
# JWT tokens issued by the application itself for the old admin app
|
4
|
+
class SecretDecoder
|
5
|
+
ALGORITHM = "HS512".freeze
|
6
|
+
|
7
|
+
def initialize(secret, algorithm = ALGORITHM)
|
8
|
+
@secret = secret
|
9
|
+
@algorithm = algorithm
|
10
|
+
end
|
11
|
+
|
12
|
+
def decode(token, validate = true)
|
13
|
+
JWT.decode(
|
14
|
+
token,
|
15
|
+
@secret,
|
16
|
+
validate,
|
17
|
+
{ algorithm: @algorithm },
|
18
|
+
)
|
19
|
+
end
|
20
|
+
|
21
|
+
def decode!(token)
|
22
|
+
token = decode(token)
|
23
|
+
raise AtomicAdmin::JwtToken::InvalidTokenError, "Unable to decode jwt token" if token.blank?
|
24
|
+
raise AtomicAdmin::JwtToken::InvalidTokenError, "Invalid token payload" if token.empty?
|
25
|
+
|
26
|
+
token[0]
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -1,69 +1,9 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
1
|
+
require_relative 'jwt_token/jwks_decoder'
|
2
|
+
require_relative 'jwt_token/secret_decoder'
|
3
|
+
|
4
4
|
module AtomicAdmin
|
5
5
|
module JwtToken
|
6
|
-
|
7
|
-
ALGORITHM = "HS512".freeze
|
8
|
-
|
9
6
|
class InvalidTokenError < StandardError; end
|
10
|
-
|
11
|
-
def self.valid?(token, secret = nil, algorithm = ALGORITHM)
|
12
|
-
decode(token, secret, true, algorithm)
|
13
|
-
end
|
14
|
-
|
15
|
-
def self.decode(token, secret = nil, validate = true, algorithm = ALGORITHM)
|
16
|
-
JWT.decode(
|
17
|
-
token,
|
18
|
-
secret || Rails.application.secrets.auth0_client_secret,
|
19
|
-
validate,
|
20
|
-
{ algorithm: algorithm },
|
21
|
-
)
|
22
|
-
end
|
23
|
-
|
24
|
-
def decoded_jwt_token(req, secret = nil)
|
25
|
-
token = AtomicAdmin::JwtToken.valid?(encoded_token(req), secret)
|
26
|
-
raise InvalidTokenError, "Unable to decode jwt token" if token.blank?
|
27
|
-
raise InvalidTokenError, "Invalid token payload" if token.empty?
|
28
|
-
|
29
|
-
token[0]
|
30
|
-
end
|
31
|
-
|
32
|
-
def validate_token
|
33
|
-
token = decoded_jwt_token(request)
|
34
|
-
raise InvalidTokenError if Rails.application.secrets.auth0_client_id != token["aud"]
|
35
|
-
|
36
|
-
current_application_instance_id = request.env['atomic.validated.application_instance_id']
|
37
|
-
if current_application_instance_id && current_application_instance_id != token["application_instance_id"]
|
38
|
-
raise InvalidTokenError
|
39
|
-
end
|
40
|
-
|
41
|
-
@user_tenant = token["user_tenant"] if token["user_tenant"].present?
|
42
|
-
@user = User.find(token["user_id"])
|
43
|
-
|
44
|
-
sign_in(@user, event: :authentication, store: false)
|
45
|
-
rescue JWT::DecodeError, InvalidTokenError => e
|
46
|
-
Rails.logger.error "JWT Error occured #{e.inspect}"
|
47
|
-
begin
|
48
|
-
render json: { error: "Unauthorized: Invalid token." }, status: :unauthorized
|
49
|
-
rescue NoMethodError
|
50
|
-
raise GraphQL::ExecutionError, "Unauthorized: Invalid token."
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
protected
|
55
|
-
|
56
|
-
def encoded_token(req)
|
57
|
-
return req.params[:jwt] if req.params[:jwt]
|
58
|
-
|
59
|
-
header = req.headers["Authorization"] || req.headers[:authorization]
|
60
|
-
raise InvalidTokenError, "No authorization header found" if header.nil?
|
61
|
-
|
62
|
-
token = header.split(" ").last
|
63
|
-
raise InvalidTokenError, "Invalid authorization header string" if token.nil?
|
64
|
-
|
65
|
-
token
|
66
|
-
end
|
67
|
-
|
7
|
+
class MissingTokenError < StandardError; end
|
68
8
|
end
|
69
|
-
end
|
9
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module AtomicAdmin::Schema
|
2
|
+
class ApplicationInstanceConfigurationSchema < ApplicationInstanceSchema
|
3
|
+
def schema
|
4
|
+
{
|
5
|
+
type: "object",
|
6
|
+
properties: {
|
7
|
+
config: {
|
8
|
+
type: "object",
|
9
|
+
},
|
10
|
+
lti_config: {
|
11
|
+
type: "object",
|
12
|
+
},
|
13
|
+
},
|
14
|
+
}
|
15
|
+
end
|
16
|
+
|
17
|
+
def uischema
|
18
|
+
{
|
19
|
+
type: "VerticalLayout",
|
20
|
+
elements: [
|
21
|
+
{
|
22
|
+
type: "Group",
|
23
|
+
label: "Custom App Instance Configuration",
|
24
|
+
elements: [
|
25
|
+
{
|
26
|
+
type: "Control",
|
27
|
+
scope: "#/properties/config",
|
28
|
+
options: {
|
29
|
+
format: "json",
|
30
|
+
props: {
|
31
|
+
size: "full",
|
32
|
+
label: "",
|
33
|
+
height: "400px",
|
34
|
+
},
|
35
|
+
},
|
36
|
+
},
|
37
|
+
],
|
38
|
+
},
|
39
|
+
{
|
40
|
+
type: "Group",
|
41
|
+
label: "LTI Config",
|
42
|
+
elements: [
|
43
|
+
{
|
44
|
+
type: "Control",
|
45
|
+
scope: "#/properties/lti_config",
|
46
|
+
options: {
|
47
|
+
format: "json",
|
48
|
+
props: {
|
49
|
+
size: "full",
|
50
|
+
label: "",
|
51
|
+
height: "400px",
|
52
|
+
},
|
53
|
+
},
|
54
|
+
},
|
55
|
+
],
|
56
|
+
},
|
57
|
+
],
|
58
|
+
}
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
|
2
|
+
module AtomicAdmin::Schema
|
3
|
+
class ApplicationInstanceCreateSchema
|
4
|
+
attr_accessor :application
|
5
|
+
|
6
|
+
def initialize(application)
|
7
|
+
@application = application
|
8
|
+
end
|
9
|
+
|
10
|
+
def schema
|
11
|
+
sites = Site.all
|
12
|
+
|
13
|
+
{
|
14
|
+
type: "object",
|
15
|
+
required: ["nickname", "site_id", "lti_key"],
|
16
|
+
properties: {
|
17
|
+
nickname: {
|
18
|
+
type: "string",
|
19
|
+
minLength: 1,
|
20
|
+
},
|
21
|
+
primary_contact: {
|
22
|
+
type: ["string", "null"],
|
23
|
+
},
|
24
|
+
lti_key: {
|
25
|
+
type: "string",
|
26
|
+
},
|
27
|
+
site_id: {
|
28
|
+
type: "number",
|
29
|
+
oneOf: sites.map do |site|
|
30
|
+
{
|
31
|
+
title: site.url,
|
32
|
+
const: site.id
|
33
|
+
}
|
34
|
+
end
|
35
|
+
},
|
36
|
+
},
|
37
|
+
}
|
38
|
+
end
|
39
|
+
|
40
|
+
def uischema
|
41
|
+
{
|
42
|
+
type: "VerticalLayout",
|
43
|
+
elements: [
|
44
|
+
{
|
45
|
+
type: "HorizontalLayout",
|
46
|
+
elements: [
|
47
|
+
{
|
48
|
+
type: "Control",
|
49
|
+
scope: "#/properties/nickname",
|
50
|
+
},
|
51
|
+
{
|
52
|
+
type: "Control",
|
53
|
+
scope: "#/properties/primary_contact",
|
54
|
+
},
|
55
|
+
],
|
56
|
+
},
|
57
|
+
{
|
58
|
+
type: "HorizontalLayout",
|
59
|
+
elements: [
|
60
|
+
{
|
61
|
+
type: "Control",
|
62
|
+
scope: "#/properties/site_id",
|
63
|
+
props: {
|
64
|
+
label: "Site"
|
65
|
+
}
|
66
|
+
},
|
67
|
+
{
|
68
|
+
type: "Control",
|
69
|
+
scope: "#/properties/lti_key",
|
70
|
+
},
|
71
|
+
]
|
72
|
+
}
|
73
|
+
]
|
74
|
+
}
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,156 @@
|
|
1
|
+
module AtomicAdmin::Schema
|
2
|
+
class ApplicationInstanceGeneralSettingsSchema < ApplicationInstanceSchema
|
3
|
+
|
4
|
+
def schema
|
5
|
+
sites = Site.all
|
6
|
+
|
7
|
+
{
|
8
|
+
type: "object",
|
9
|
+
properties: {
|
10
|
+
nickname: {
|
11
|
+
type: "string",
|
12
|
+
minLength: 1,
|
13
|
+
},
|
14
|
+
primary_contact: {
|
15
|
+
type: ["string", "null"],
|
16
|
+
},
|
17
|
+
created_at: {
|
18
|
+
type: "string",
|
19
|
+
format: "date-time",
|
20
|
+
},
|
21
|
+
lti_key: {
|
22
|
+
type: "string",
|
23
|
+
},
|
24
|
+
lti_secret: {
|
25
|
+
type: "string",
|
26
|
+
},
|
27
|
+
# The available sites is based on the sites that have been created
|
28
|
+
# so this would require it to be dynamically generated on the fly
|
29
|
+
site_id: {
|
30
|
+
type: "number",
|
31
|
+
oneOf: sites.map do |site|
|
32
|
+
{
|
33
|
+
title: site.url,
|
34
|
+
const: site.id
|
35
|
+
}
|
36
|
+
end
|
37
|
+
},
|
38
|
+
domain: {
|
39
|
+
type: "string",
|
40
|
+
},
|
41
|
+
canvas_token: {
|
42
|
+
type: ["string", "null"],
|
43
|
+
},
|
44
|
+
rollbar_enabled: {
|
45
|
+
type: "boolean",
|
46
|
+
},
|
47
|
+
use_scoped_developer_key: {
|
48
|
+
type: "boolean",
|
49
|
+
},
|
50
|
+
},
|
51
|
+
required: ["nickname"],
|
52
|
+
}
|
53
|
+
end
|
54
|
+
|
55
|
+
def uischema
|
56
|
+
token_preview = @application_instance.canvas_token_preview() || "Not set"
|
57
|
+
{
|
58
|
+
type: "HorizontalLayout",
|
59
|
+
elements: [
|
60
|
+
{
|
61
|
+
type: "Group",
|
62
|
+
label: "General Settings",
|
63
|
+
elements: [
|
64
|
+
{
|
65
|
+
type: "VerticalLayout",
|
66
|
+
elements: [
|
67
|
+
{
|
68
|
+
type: "Control",
|
69
|
+
scope: "#/properties/nickname",
|
70
|
+
},
|
71
|
+
{
|
72
|
+
type: "Control",
|
73
|
+
scope: "#/properties/primary_contact",
|
74
|
+
},
|
75
|
+
{
|
76
|
+
type: "Control",
|
77
|
+
scope: "#/properties/created_at",
|
78
|
+
label: "Date Created",
|
79
|
+
options: {
|
80
|
+
format: "date",
|
81
|
+
props: {
|
82
|
+
isReadOnly: true,
|
83
|
+
size: "small",
|
84
|
+
},
|
85
|
+
},
|
86
|
+
},
|
87
|
+
{
|
88
|
+
type: "Control",
|
89
|
+
scope: "#/properties/site_id",
|
90
|
+
label: "LMS URL",
|
91
|
+
options: {
|
92
|
+
props: {
|
93
|
+
menuSize: "auto",
|
94
|
+
}
|
95
|
+
}
|
96
|
+
},
|
97
|
+
{
|
98
|
+
type: "Control",
|
99
|
+
scope: "#/properties/domain",
|
100
|
+
label: "LTI Tool Domain",
|
101
|
+
},
|
102
|
+
{
|
103
|
+
type: "Control",
|
104
|
+
scope: "#/properties/canvas_token",
|
105
|
+
label: "Canvas Token",
|
106
|
+
options: {
|
107
|
+
props: {
|
108
|
+
message: "Current Canvas Token: #{token_preview}",
|
109
|
+
},
|
110
|
+
},
|
111
|
+
},
|
112
|
+
{
|
113
|
+
type: "Control",
|
114
|
+
scope: "#/properties/rollbar_enabled",
|
115
|
+
label: "Enable Rollbar",
|
116
|
+
},
|
117
|
+
{
|
118
|
+
type: "Control",
|
119
|
+
scope: "#/properties/use_scoped_developer_key",
|
120
|
+
label: "Use Scoped Developer Key",
|
121
|
+
},
|
122
|
+
],
|
123
|
+
},
|
124
|
+
],
|
125
|
+
},
|
126
|
+
{
|
127
|
+
type: "Group",
|
128
|
+
label: "LTI Key and Secret",
|
129
|
+
elements: [
|
130
|
+
{
|
131
|
+
type: "VerticalLayout",
|
132
|
+
elements: [
|
133
|
+
{
|
134
|
+
type: "Control",
|
135
|
+
scope: "#/properties/lti_key",
|
136
|
+
label: "LTI Key",
|
137
|
+
options: {
|
138
|
+
props: {
|
139
|
+
isReadOnly: true,
|
140
|
+
},
|
141
|
+
},
|
142
|
+
},
|
143
|
+
{
|
144
|
+
type: "Control",
|
145
|
+
scope: "#/properties/lti_secret",
|
146
|
+
label: "LTI Secret",
|
147
|
+
},
|
148
|
+
],
|
149
|
+
},
|
150
|
+
],
|
151
|
+
},
|
152
|
+
],
|
153
|
+
}
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|