panda_pal 5.1.0 → 5.2.0
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 +139 -93
- data/app/controllers/panda_pal/lti_controller.rb +0 -18
- data/app/controllers/panda_pal/lti_v1_p0_controller.rb +34 -0
- data/app/controllers/panda_pal/lti_v1_p3_controller.rb +98 -0
- data/app/lib/lti_xml/base_platform.rb +4 -4
- data/app/lib/panda_pal/launch_url_helpers.rb +69 -0
- data/app/lib/panda_pal/lti_jwt_validator.rb +88 -0
- data/app/lib/panda_pal/misc_helper.rb +11 -0
- data/app/models/panda_pal/organization.rb +6 -3
- data/app/models/panda_pal/{organization → organization_concerns}/settings_validation.rb +21 -5
- data/app/models/panda_pal/{organization → organization_concerns}/task_scheduling.rb +32 -0
- data/app/models/panda_pal/platform.rb +40 -0
- data/app/views/panda_pal/lti_v1_p3/login.html.erb +1 -0
- data/app/views/panda_pal/partials/_auto_submit_form.html.erb +9 -0
- data/config/dev_lti_key.key +27 -0
- data/config/routes.rb +12 -2
- data/db/migrate/20160412205931_create_panda_pal_organizations.rb +1 -1
- data/db/migrate/20160413135653_create_panda_pal_sessions.rb +1 -1
- data/db/migrate/20160425130344_add_panda_pal_organization_to_session.rb +1 -1
- data/db/migrate/20170106165533_add_salesforce_id_to_organizations.rb +1 -1
- data/db/migrate/20171205183457_encrypt_organization_settings.rb +1 -1
- data/db/migrate/20171205194657_remove_old_organization_settings.rb +8 -3
- data/lib/panda_pal.rb +28 -15
- data/lib/panda_pal/engine.rb +8 -39
- data/lib/panda_pal/helpers.rb +1 -0
- data/lib/panda_pal/helpers/controller_helper.rb +138 -44
- data/lib/panda_pal/helpers/route_helper.rb +8 -8
- data/lib/panda_pal/helpers/secure_headers.rb +79 -0
- data/lib/panda_pal/version.rb +1 -1
- data/panda_pal.gemspec +3 -2
- metadata +32 -8
@@ -76,7 +76,7 @@ module LtiXml
|
|
76
76
|
|
77
77
|
def add_lti_nav
|
78
78
|
PandaPal.lti_paths.each do |k, v|
|
79
|
-
@tc.set_ext_param(platform, k.to_sym, ext_params(v))
|
79
|
+
@tc.set_ext_param(platform, k.to_sym, ext_params(v, k))
|
80
80
|
end
|
81
81
|
end
|
82
82
|
|
@@ -84,9 +84,9 @@ module LtiXml
|
|
84
84
|
@tc.set_ext_param(platform, :environments, PandaPal.lti_environments)
|
85
85
|
end
|
86
86
|
|
87
|
-
def ext_params(options)
|
88
|
-
|
89
|
-
options[:url] =
|
87
|
+
def ext_params(options, k)
|
88
|
+
options.delete(:route_helper_key)
|
89
|
+
options[:url] = PandaPal::LaunchUrlHelpers.absolute_launch_url(k.to_sym, host: parsed_request_url, launch_handler: nil)
|
90
90
|
options
|
91
91
|
end
|
92
92
|
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module PandaPal
|
2
|
+
module LaunchUrlHelpers
|
3
|
+
def self.absolute_launch_url(launch_type, host:, launch_handler: nil)
|
4
|
+
opts = PandaPal.lti_paths[launch_type]
|
5
|
+
final_url = launch_url(opts, launch_type: launch_type)
|
6
|
+
|
7
|
+
is_direct = opts[:route_helper_key].present? || !launch_handler.present?
|
8
|
+
|
9
|
+
if is_direct
|
10
|
+
return final_url if URI.parse(final_url).absolute?
|
11
|
+
return [host.to_s, final_url].join
|
12
|
+
else
|
13
|
+
launch_handler = resolve_route(launch_handler) if launch_handler.is_a?(Symbol)
|
14
|
+
return add_url_params([host.to_s, launch_handler].join, {
|
15
|
+
launch_type: launch_type,
|
16
|
+
})
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.launch_url(opts, launch_type: nil)
|
21
|
+
url = launch_route(opts, launch_type: launch_type)
|
22
|
+
url = resolve_url_symbol(url) if url.is_a?(Symbol)
|
23
|
+
url
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.launch_route(opts, launch_type: nil)
|
27
|
+
if opts.is_a?(Symbol) || opts.is_a?(String)
|
28
|
+
launch_type = opts.to_sym
|
29
|
+
opts = PandaPal.lti_paths[launch_type]
|
30
|
+
end
|
31
|
+
|
32
|
+
if opts[:route_helper_key]
|
33
|
+
opts[:route_helper_key].to_sym
|
34
|
+
else
|
35
|
+
opts[:url] ||= launch_type
|
36
|
+
opts[:url]
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.resolve_url_symbol(sym, **opts)
|
41
|
+
sym = :"#{sym}_url" unless sym.to_s.ends_with?('_url')
|
42
|
+
opts[:only_path] = true unless opts.key?(:only_path)
|
43
|
+
resolve_route(:"MainApp/#{sym}", **opts)
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.add_url_params(url, params)
|
47
|
+
uri = URI(url)
|
48
|
+
decoded_params = URI.decode_www_form(uri.query || "")
|
49
|
+
params.each do |k, v|
|
50
|
+
decoded_params << [k, v]
|
51
|
+
end
|
52
|
+
uri.query = URI.encode_www_form(decoded_params)
|
53
|
+
uri.to_s
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def self.resolve_route(key, *arguments, engine: 'PandaPal', **kwargs)
|
59
|
+
return key if key.is_a?(String)
|
60
|
+
|
61
|
+
key_bits = key.to_s.split('/')
|
62
|
+
key_bits.prepend(engine) if key_bits.count == 1
|
63
|
+
key_bits[0] = key_bits[0].classify
|
64
|
+
|
65
|
+
engine = key_bits[0] == 'MainApp' ? Rails.application : (key_bits[0].constantize)::Engine
|
66
|
+
engine.routes.url_helpers.send(key_bits[1], *arguments, **kwargs)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
module PandaPal
|
2
|
+
class LtiJwtValidator
|
3
|
+
attr_reader :errors
|
4
|
+
|
5
|
+
def initialize(jwt, client_id)
|
6
|
+
@jwt = jwt
|
7
|
+
@client_id = client_id
|
8
|
+
@errors = []
|
9
|
+
end
|
10
|
+
|
11
|
+
def valid?
|
12
|
+
verify_audience(@jwt)
|
13
|
+
verify_sub(@jwt)
|
14
|
+
verify_nonce(@jwt)
|
15
|
+
verify_issued_at(@jwt)
|
16
|
+
verify_expiration(@jwt)
|
17
|
+
errors.empty?
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def verify_audience(jwt)
|
23
|
+
aud = jwt[:aud]
|
24
|
+
aud_array = [*aud]
|
25
|
+
errors << 'Audience must be a string or Array of strings.' unless aud_array.all? { |a| a.is_a? String }
|
26
|
+
if jwt.key? :azp
|
27
|
+
verify_azp(aud, jwt[:azp])
|
28
|
+
else
|
29
|
+
errors << 'Audience not found' unless public_key_matches_one_of_client_ids(aud)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def verify_azp(aud, azp)
|
34
|
+
errors << 'Audience does not contain/match Authorized Party' unless azp_in_aud(aud, azp)
|
35
|
+
errors << 'Audience does not match Platform client_id' unless public_key_matches_one_of_client_ids(aud)
|
36
|
+
end
|
37
|
+
|
38
|
+
def verify_sub(jwt)
|
39
|
+
errors << 'Subject not present' if jwt[:sub].blank?
|
40
|
+
end
|
41
|
+
|
42
|
+
def verify_issued_at(jwt)
|
43
|
+
lower_bound = issued_at_lower_bound
|
44
|
+
errors << "Issued at of #{jwt[:iat]} not between #{lower_bound.to_i} and #{Time.zone.now.to_i}" unless Time.zone.at(
|
45
|
+
jwt[:iat]
|
46
|
+
).between?(
|
47
|
+
lower_bound, Time.zone.now
|
48
|
+
)
|
49
|
+
end
|
50
|
+
|
51
|
+
def verify_expiration(jwt)
|
52
|
+
now = Time.zone.now
|
53
|
+
errors << "Expiration time of #{jwt[:exp]} before #{now.to_i}" unless Time.zone.at(jwt[:exp]) > Time.zone.now
|
54
|
+
end
|
55
|
+
|
56
|
+
def verify_nonce(jwt)
|
57
|
+
unless jwt[:nonce].present?
|
58
|
+
errors << 'Nonce not present'
|
59
|
+
return
|
60
|
+
end
|
61
|
+
|
62
|
+
return unless defined?(Redis) && Redis.current
|
63
|
+
|
64
|
+
cache_key = "nonces/#{jwt[:nonce]}"
|
65
|
+
if Redis.current.get(cache_key)
|
66
|
+
errors << 'Nonce has been used already'
|
67
|
+
else
|
68
|
+
Redis.current.set(cache_key, Time.zone.now.to_i, ex: 5.minutes)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def azp_in_aud(aud, azp)
|
73
|
+
if aud.is_a? Array
|
74
|
+
aud.include? azp
|
75
|
+
else
|
76
|
+
aud == azp
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def issued_at_lower_bound
|
81
|
+
ENV.key?('issued_at_minutes_ago') ? ENV['issued_at_minutes_ago'].minutes.ago : 10.minutes.ago
|
82
|
+
end
|
83
|
+
|
84
|
+
def public_key_matches_one_of_client_ids(aud)
|
85
|
+
(Array(aud) & Array(@client_id)).any?
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -1,5 +1,3 @@
|
|
1
|
-
Dir[File.dirname(__FILE__) + "/organization/*.rb"].each { |file| require file }
|
2
|
-
|
3
1
|
module PandaPal
|
4
2
|
module OrganizationConcerns; end
|
5
3
|
|
@@ -7,7 +5,6 @@ module PandaPal
|
|
7
5
|
include OrganizationConcerns::SettingsValidation
|
8
6
|
include OrganizationConcerns::TaskScheduling if defined?(Sidekiq.schedule)
|
9
7
|
|
10
|
-
attribute :settings
|
11
8
|
serialize :settings, Hash
|
12
9
|
attr_encrypted :settings, marshal: true, key: :encryption_key
|
13
10
|
before_save {|a| a.settings = a.settings} # this is a hacky work-around to a bug where attr_encrypted is not saving settings in place
|
@@ -21,6 +18,12 @@ module PandaPal
|
|
21
18
|
after_create :create_schema
|
22
19
|
after_commit :destroy_schema, on: :destroy
|
23
20
|
|
21
|
+
if defined?(scheduled_task)
|
22
|
+
scheduled_task '0 0 3 * * *', :clean_old_sessions do
|
23
|
+
PandaPal::Session.where(panda_pal_organization: self).where('updated_at < ?', 1.week.ago).delete_all
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
24
27
|
before_validation on: [:update] do
|
25
28
|
errors.add(:name, 'should not be changed after creation') if name_changed?
|
26
29
|
end
|
@@ -52,15 +52,31 @@ module PandaPal
|
|
52
52
|
|
53
53
|
if settings.nil?
|
54
54
|
errors << "Entry #{human_path} is required" if spec[:required]
|
55
|
-
return
|
55
|
+
return errors
|
56
56
|
end
|
57
57
|
|
58
58
|
if spec[:type]
|
59
|
-
|
60
|
-
|
61
|
-
|
59
|
+
norm_types = Array(spec[:type]).map do |t|
|
60
|
+
if [:bool, :boolean, 'Bool', 'Boolean'].include?(t)
|
61
|
+
'Boolean'
|
62
|
+
elsif t.is_a?(String)
|
63
|
+
t.constantize
|
64
|
+
else
|
65
|
+
t
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
any_match = norm_types.any? do |t|
|
70
|
+
if t == 'Boolean'
|
71
|
+
settings == true || settings == false
|
72
|
+
else
|
73
|
+
settings.is_a?(t)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
unless any_match
|
62
78
|
errors << "Expected #{human_path} to be a #{spec[:type]}. Was a #{settings.class.to_s}"
|
63
|
-
return
|
79
|
+
return errors
|
64
80
|
end
|
65
81
|
end
|
66
82
|
|
@@ -36,9 +36,36 @@ module PandaPal
|
|
36
36
|
type: 'Hash',
|
37
37
|
required: false,
|
38
38
|
properties: _schedule_descriptors.keys.reduce({}) do |hash, k|
|
39
|
+
desc = _schedule_descriptors[k]
|
40
|
+
|
39
41
|
hash.tap do |hash|
|
42
|
+
kl = ' ' * (k.to_s.length - 4)
|
40
43
|
hash[k.to_sym] = hash[k.to_s] = {
|
41
44
|
required: false,
|
45
|
+
description: <<~MARKDOWN,
|
46
|
+
Override schedule for '#{k.to_s}' task.
|
47
|
+
|
48
|
+
**Default**: #{desc[:schedule].is_a?(String) ? desc[:schedule] : '<Computed>'}
|
49
|
+
|
50
|
+
Set to `false` to disable or supply a Cron string:
|
51
|
+
```yaml
|
52
|
+
#{k.to_s}: 0 0 0 * * * America/Denver
|
53
|
+
##{kl} │ │ │ │ │ │ └── Timezone (Optional)
|
54
|
+
##{kl} │ │ │ │ │ └── Day of Week
|
55
|
+
##{kl} │ │ │ │ └── Month
|
56
|
+
##{kl} │ │ │ └── Day of Month
|
57
|
+
##{kl} │ │ └── Hour
|
58
|
+
##{kl} │ └── Minute
|
59
|
+
##{kl} └── Second (Optional)
|
60
|
+
````
|
61
|
+
MARKDOWN
|
62
|
+
json_schema: {
|
63
|
+
oneOf: [
|
64
|
+
{ type: 'string', pattern: '^((((\d+,)+\d+|(\d+(\/|-)\d+)|\d+|\*) ?){5,6})(\w+\/\w+)?$' },
|
65
|
+
{ enum: [false] },
|
66
|
+
],
|
67
|
+
default: desc[:schedule].is_a?(String) ? desc[:schedule] : '0 0 3 * * * America/Denver',
|
68
|
+
},
|
42
69
|
validate: ->(value, *args, errors:, **kwargs) {
|
43
70
|
begin
|
44
71
|
Rufus::Scheduler.parse(value) if value
|
@@ -66,6 +93,11 @@ module PandaPal
|
|
66
93
|
}
|
67
94
|
end
|
68
95
|
|
96
|
+
def remove_scheduled_task(name_or_method)
|
97
|
+
dval = _schedule_descriptors.delete(name_or_method.to_s)
|
98
|
+
Rails.logger.warn("No task with key '#{name_or_method}' to delete!") unless dval.present?
|
99
|
+
end
|
100
|
+
|
69
101
|
def sync_schedules
|
70
102
|
# Ensure deleted Orgs are removed
|
71
103
|
existing_orgs = pluck(:name)
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module PandaPal
|
2
|
+
class Platform
|
3
|
+
def public_jwks
|
4
|
+
response = HTTParty.get(jwks_url)
|
5
|
+
return nil unless response.success?
|
6
|
+
|
7
|
+
JSON::JWK::Set.new(JSON.parse(response.body))
|
8
|
+
rescue
|
9
|
+
nil
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class Platform::Canvas < Platform
|
14
|
+
attr_accessor :base_url
|
15
|
+
|
16
|
+
def initialize(base_url)
|
17
|
+
@base_url = base_url
|
18
|
+
end
|
19
|
+
|
20
|
+
def host
|
21
|
+
base_url
|
22
|
+
end
|
23
|
+
|
24
|
+
def jwks_url
|
25
|
+
"#{base_url}/api/lti/security/jwks"
|
26
|
+
end
|
27
|
+
|
28
|
+
def authentication_redirect_url
|
29
|
+
"#{base_url}/api/lti/authorize_redirect"
|
30
|
+
end
|
31
|
+
|
32
|
+
def grant_url
|
33
|
+
"#{base_url}/login/oauth2/token"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
class Platform
|
38
|
+
CANVAS = Platform::Canvas.new('https://canvas.instructure.com')
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
<%= render "panda_pal/partials/auto_submit_form" %>
|
@@ -0,0 +1,9 @@
|
|
1
|
+
<%= form_tag(@form_action, method: @method, id: 'redirect-form') do %>
|
2
|
+
<% @form_data.each do |k, v| %>
|
3
|
+
<%= hidden_field_tag(k, v) %>
|
4
|
+
<% end %>
|
5
|
+
<% end %>
|
6
|
+
|
7
|
+
<script type="text/javascript" nonce="<%= content_security_policy_script_nonce %>">
|
8
|
+
document.getElementById('redirect-form').submit();
|
9
|
+
</script>
|
@@ -0,0 +1,27 @@
|
|
1
|
+
-----BEGIN RSA PRIVATE KEY-----
|
2
|
+
MIIEowIBAAKCAQEAt+Za2scYD223YjVvAeuxpYvBjTf4yp2Y3udPFllG85zIVH/K
|
3
|
+
XKsP4QVP9de/fh4FzouZPOfXMDY1KmWSlMK9jNYUm2rh0zp7+X/F6cnc5QMCzbqV
|
4
|
+
MNnfALI4kvGM8yNPLbMJx4nA47T7T9TmlMJUZQxtyvC8zxBMv8Mv4Ae0arXevpI1
|
5
|
+
Vcm/3Huz6dAJPRjTnL2CBsXrmNzf51OJU5r27HYFE4367g9njix1lGOcQqVDVjnT
|
6
|
+
uXFTlidfUa2Bp2v5owzZU7Fe9jSQmeAQpqQ/XdYzWQNEJPrz8ESz5jP3brJ4q5Y5
|
7
|
+
FaK9l8DHILqomGWhttYQ6IMCfdpLb4D4B2gcLQIDAQABAoIBAAuaXislmrAGhSaO
|
8
|
+
JoXhgCDo03p8iJcIIIgX4haP5XkjcERcl8EHDgZtlmD1juB/NnCUwENmgV5KXUpi
|
9
|
+
hEAclWcYbs5rjPoN25qfZDZfBS/x47BlUFp3tKlPlWA4G2OP28QPYtOTLndviNe9
|
10
|
+
oBrMtBR4F0lRrSgHaEBFKXUiJ1EAMycKcXab63HA7Fp9HArLAu0NM0VWCsJBmxB/
|
11
|
+
BprW4LtYRG7iJl9kfsHyAzOaAV5H1zLqhfA2YvEI2XADrILQtpPwZpmNKq/5L5On
|
12
|
+
RlGy1WcBgxj8cqWdqir1PxVN/xDKVnUT9Mrf+SHn7xzgDL2uXvhD1Qt/5iLeVlvN
|
13
|
+
nQTNJyECgYEA681T7A0oj9/FuqLwE1gxm+RUczv+EyXcNvHCUkpFSaiyEyqdQB9f
|
14
|
+
EpHgeg7pXmlRmUwNkT3ZAH7O0uv3CVR6b8j9N7XmYbmcuRU2A6loeiBB/HkOOt21
|
15
|
+
hxiC+tXD/K7c5BfRkThL6ca213xqztbqxxj/C8qGnWVaLc7SLPlJZfUCgYEAx6bp
|
16
|
+
7tRGq8Q5xkyKvSDUSln9MI/iZjSysd44rTtj8Vo9LfQsBi5JbWm0+UEtaZoabeC1
|
17
|
+
xaAp/ZETLe4oGu7CJxBreSE+UE/a6zc5MIcJblP+8RIiAYkf67iHNwSbiqSBvPqj
|
18
|
+
S0HFxUZqw1NigmqsptHrPtvuTUZI8Hx3hKMgwlkCgYAa8RrlnZtE1QyChptnmmwQ
|
19
|
+
o8YCZJhjF7BRls3dGR9RizTNe9D7wpnaRVCgoZOIdgAcw9PJBIgGxnZbIxrWthBH
|
20
|
+
NW+5Lc9k2xBNFV9Wi8SkL4tajXpSv4I+LU7J2iLKfDBA33fSX9xMmafKdyy89VFd
|
21
|
+
7j01264Fzc6/7SGWgeUhAQKBgDKGSgMXkz7arKhDLIUKLs8WEN3eO7QTt/kNPJiS
|
22
|
+
RAuLA5qChTWXNxvKOXMujFiCGBggWr/FdXrm4MypzVpre5S5Mgl4YTWfz83grsda
|
23
|
+
FQfnl8fYB+UNl5dmnklNEDO4x+BUKUjdPzhaRqBhlLdeWYzp6LeCnr7Nf53kUbau
|
24
|
+
NZcZAoGBAJcYyWegsQCEVMY/ck42uNmZ00DmK1XSAU7hYulBo8iD3OqvXZ7Etppw
|
25
|
+
SaRhSTCoTsLBWlqGccNDGoXH/r2/jJAFb8hUjGa2Z5dKP9byWMONEQEh6g8wzL/w
|
26
|
+
XRthIsgdIE58a+cJGwHh/raUQwvD72S1C/epQSYcLe3TdUumtZm/
|
27
|
+
-----END RSA PRIVATE KEY-----
|
data/config/routes.rb
CHANGED
@@ -1,4 +1,14 @@
|
|
1
1
|
PandaPal::Engine.routes.draw do
|
2
|
-
get '/config' => '
|
3
|
-
|
2
|
+
get '/config' => 'lti_v1_p0#tool_config' # Legacy Support
|
3
|
+
|
4
|
+
scope '/v1p0', as: 'v1p0' do
|
5
|
+
get '/config' => 'lti_v1_p0#tool_config'
|
6
|
+
end
|
7
|
+
|
8
|
+
scope '/v1p3', as: 'v1p3' do
|
9
|
+
get '/config' => 'lti_v1_p3#tool_config'
|
10
|
+
post '/oidc_login' => 'lti_v1_p3#login'
|
11
|
+
post '/resource_link_request' => 'lti_v1_p3#resource_link_request'
|
12
|
+
get '/public_jwks' => 'lti_v1_p3#public_jwks'
|
13
|
+
end
|
4
14
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
class AddPandaPalOrganizationToSession < ActiveRecord::Migration
|
1
|
+
class AddPandaPalOrganizationToSession < ActiveRecord::Migration
|
2
2
|
def change
|
3
3
|
add_column :panda_pal_sessions, :panda_pal_organization_id, :integer
|
4
4
|
add_index :panda_pal_sessions, :panda_pal_organization_id
|
@@ -1,4 +1,4 @@
|
|
1
|
-
class EncryptOrganizationSettings < ActiveRecord::Migration
|
1
|
+
class EncryptOrganizationSettings < ActiveRecord::Migration
|
2
2
|
def up
|
3
3
|
# don't rerun this if it was already run before we renamed the migration.
|
4
4
|
existing_versions = execute ("SELECT * from schema_migrations where version = '30171205183457'")
|
@@ -1,4 +1,4 @@
|
|
1
|
-
class RemoveOldOrganizationSettings < ActiveRecord::Migration
|
1
|
+
class RemoveOldOrganizationSettings < ActiveRecord::Migration
|
2
2
|
def current_tenant
|
3
3
|
@current_tenant ||= PandaPal::Organization.find_by_name(Apartment::Tenant.current)
|
4
4
|
end
|
@@ -10,14 +10,16 @@ class RemoveOldOrganizationSettings < ActiveRecord::Migration[5.1]
|
|
10
10
|
execute "DELETE from schema_migrations where version = '30171205194657'"
|
11
11
|
return
|
12
12
|
end
|
13
|
+
|
13
14
|
# migrations run for public and local tenants. However, PandaPal::Organization
|
14
15
|
# is going to always go to public tenant. So don't do this active record
|
15
16
|
# stuff unless we are on the public tenant.
|
16
|
-
if
|
17
|
+
if Apartment::Tenant.current == 'public'
|
17
18
|
#PandaPal::Organization.connection.schema_cache.clear!
|
18
19
|
#PandaPal::Organization.reset_column_information
|
19
20
|
PandaPal::Organization.find_each do |o|
|
20
21
|
# Would like to just be able to do this:
|
22
|
+
# PandaPal::Organization.reset_column_information
|
21
23
|
# o.settings = YAML.load(o.old_settings)
|
22
24
|
# o.save!
|
23
25
|
# but for some reason that is always making the settings null. Instead we will encrypt the settings manually.
|
@@ -26,14 +28,17 @@ class RemoveOldOrganizationSettings < ActiveRecord::Migration[5.1]
|
|
26
28
|
key = o.encryption_key
|
27
29
|
encrypted_settings = PandaPal::Organization.encrypt_settings(YAML.load(o.old_settings), iv: iv, key: key)
|
28
30
|
o.update_columns(encrypted_settings_iv: [iv].pack("m"), encrypted_settings: encrypted_settings)
|
31
|
+
o = PandaPal::Organization.find_by!(name: o.name)
|
32
|
+
raise "Failed to migrate PandaPal Settings" if o.settings != YAML.load(o.old_settings)
|
29
33
|
end
|
30
34
|
end
|
35
|
+
|
31
36
|
remove_column :panda_pal_organizations, :old_settings
|
32
37
|
end
|
33
38
|
|
34
39
|
def down
|
35
40
|
add_column :panda_pal_organizations, :old_settings, :text
|
36
|
-
if
|
41
|
+
if Apartment::Tenant.current == 'public'
|
37
42
|
PandaPal::Organization.find_each do |o|
|
38
43
|
o.old_settings = o.settings.to_yaml
|
39
44
|
o.save
|