Skip to content

Commit

Permalink
closes #102 #99
Browse files Browse the repository at this point in the history
  • Loading branch information
catmando committed Jan 10, 2019
1 parent 77aac1b commit a222222
Show file tree
Hide file tree
Showing 21 changed files with 315 additions and 104 deletions.
12 changes: 8 additions & 4 deletions install/rails-webpacker.rb
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,14 @@ class ApplicationRecord < ActiveRecord::Base
# useful for debugging
module Hyperstack
def self.on_error(*args)
::Rails.logger.debug "\033[0;31;1mHYPERSTACK APPLICATION ERROR: #{args.join(", ")}\n"\\
"To further investigate you may want to add a debugging "\\
"breakpoint in config/initializers/hyperstack.rb\033[0;30;21m"
def self.on_error(operation, err, params, formatted_error_message)
::Rails.logger.debug(
"#{formatted_error_message}\n\n" +
Pastel.new.red(
'To further investigate you may want to add a debugging '\
'breakpoint to the on_error method in config/initializers/hyperstack.rb'
)
)
end
end if Rails.env.development?
CODE
Expand Down
48 changes: 34 additions & 14 deletions ruby/hyper-model/Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
PATH
remote: ../hyper-component
specs:
hyper-component (1.0.alpha1.1)
hyper-state (= 1.0.alpha1.1)
hyperstack-config (= 1.0.alpha1.1)
hyper-component (1.0.alpha1.2)
hyper-state (= 1.0.alpha1.2)
hyperstack-config (= 1.0.alpha1.2)
libv8 (~> 6.3.0)
mini_racer (~> 0.1.15)
opal (>= 0.11.0, < 0.12.0)
Expand All @@ -13,23 +13,24 @@ PATH
PATH
remote: ../hyper-operation
specs:
hyper-operation (1.0.alpha1.1)
hyper-operation (1.0.alpha1.2)
activerecord (>= 4.0.0)
hyper-component (= 1.0.alpha1.1)
hyper-component (= 1.0.alpha1.2)
mutations
opal-activesupport (~> 0.3.1)
tty-table

PATH
remote: ../hyper-state
specs:
hyper-state (1.0.alpha1.1)
hyperstack-config (= 1.0.alpha1.1)
hyper-state (1.0.alpha1.2)
hyperstack-config (= 1.0.alpha1.2)
opal (>= 0.11.0, < 0.12.0)

PATH
remote: ../hyperstack-config
specs:
hyperstack-config (1.0.alpha1.1)
hyperstack-config (1.0.alpha1.2)
libv8 (~> 6.3.0)
listen (~> 3.0)
mini_racer (~> 0.1.15)
Expand All @@ -41,11 +42,11 @@ PATH
PATH
remote: .
specs:
hyper-model (1.0.alpha1.1)
hyper-model (1.0.alpha1.2)
activemodel
activerecord (>= 4.0.0)
hyper-component (= 1.0.alpha1.1)
hyper-operation (= 1.0.alpha1.1)
hyper-component (= 1.0.alpha1.2)
hyper-operation (= 1.0.alpha1.2)

GEM
remote: https://rubygems.org/
Expand Down Expand Up @@ -141,6 +142,7 @@ GEM
eventmachine (>= 0.12.9)
http_parser.rb (~> 0.6.0)
equalizer (0.0.11)
equatable (0.5.0)
erubi (1.7.1)
eventmachine (1.2.7)
execjs (2.7.0)
Expand Down Expand Up @@ -189,6 +191,7 @@ GEM
mutations (0.8.3)
activesupport
mysql2 (0.5.2)
necromancer (0.4.0)
nio4r (2.3.1)
nokogiri (1.8.4)
mini_portile2 (~> 2.3.0)
Expand Down Expand Up @@ -220,6 +223,9 @@ GEM
parallel (1.12.1)
parser (2.3.3.1)
ast (~> 2.2)
pastel (0.7.2)
equatable (~> 0.5.0)
tty-color (~> 0.4.0)
powerpack (0.1.2)
procto (0.0.3)
pry (0.11.3)
Expand Down Expand Up @@ -271,8 +277,8 @@ GEM
rake
rake (12.3.1)
rb-fsevent (0.10.3)
rb-inotify (0.9.10)
ffi (>= 0.5.0, < 2)
rb-inotify (0.10.0)
ffi (~> 1.0)
react-rails (2.4.7)
babel-transpiler (>= 0.7.0)
connection_pool
Expand Down Expand Up @@ -343,6 +349,11 @@ GEM
activesupport (>= 4.0)
sprockets (>= 3.0.0)
sqlite3 (1.3.13)
strings (0.1.4)
strings-ansi (~> 0.1.0)
unicode-display_width (~> 1.4.0)
unicode_utils (~> 1.4.0)
strings-ansi (0.1.0)
thin (1.7.2)
daemons (~> 1.0, >= 1.0.9)
eventmachine (~> 1.0, >= 1.0.4)
Expand All @@ -351,11 +362,20 @@ GEM
thread_safe (0.3.6)
tilt (2.0.8)
timecop (0.8.1)
tty-color (0.4.3)
tty-screen (0.6.5)
tty-table (0.10.0)
equatable (~> 0.5.0)
necromancer (~> 0.4.0)
pastel (~> 0.7.2)
strings (~> 0.1.0)
tty-screen (~> 0.6.4)
tzinfo (1.2.5)
thread_safe (~> 0.1)
uglifier (4.1.19)
uglifier (4.1.20)
execjs (>= 0.3.0, < 3)
unicode-display_width (1.4.0)
unicode_utils (1.4.0)
unparser (0.2.8)
abstract_type (~> 0.0.7)
adamantium (~> 0.2.0)
Expand Down
15 changes: 10 additions & 5 deletions ruby/hyper-model/lib/active_record_base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ def self._synchromesh_scope_args_check(args)
class ReactiveRecordPsuedoRelationArray < Array
attr_accessor :__synchromesh_permission_granted
attr_accessor :acting_user
def __secure_collection_check(_acting_user)
def __secure_collection_check(*)
self
end
end
Expand All @@ -39,11 +39,13 @@ def __secure_collection_check(_acting_user)
class Relation
attr_accessor :__synchromesh_permission_granted
attr_accessor :acting_user
def __secure_collection_check(acting_user)

def __secure_collection_check(cache_item)
return self if __synchromesh_permission_granted
return self if __secure_remote_access_to_all(self, acting_user).__synchromesh_permission_granted
return self if __secure_remote_access_to_unscoped(self, acting_user).__synchromesh_permission_granted
Hyperstack::InternalPolicy.raise_operation_access_violation(:scoped_permission_not_granted, "Last relation: #{self}, acting_user: #{acting_user}")
return self if __secure_remote_access_to_all(self, cache_item.acting_user).__synchromesh_permission_granted
return self if __secure_remote_access_to_unscoped(self, cache_item.acting_user).__synchromesh_permission_granted
Hyperstack::InternalPolicy.raise_operation_access_violation(
:scoped_permission_not_granted, "Access denied for #{cache_item}")
end
end
# Monkey patches and extensions to base
Expand Down Expand Up @@ -90,6 +92,9 @@ def finder_method(name, &block)
this.acting_user = old
end
end
singleton_class.send(:define_method, "_#{name}") do |*args|
all.instance_exec(*args, &block)
end
singleton_class.send(:define_method, name) do |*args|
all.instance_exec(*args, &block)
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -261,10 +261,10 @@ def unscoped
end

def finder_method(name)
ReactiveRecord::ScopeDescription.new(self, "_#{name}", {})
ReactiveRecord::ScopeDescription.new(self, "_#{name}", {}) # was adding _ to front
[name, "#{name}!"].each do |method|
singleton_class.send(:define_method, method) do |*vargs|
all.apply_scope("_#{method}", *vargs).first
all.apply_scope("_#{method}", *vargs).first # was adding _ to front
end
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -316,21 +316,23 @@ def send_save_to_server(save, validate, force, &block)

else

def self.find_record(model, id, vector, save)
def self.find_record(model, id, acting_user, vector, save)
if !save
found = vector[1..-1].inject(vector[0]) do |object, method|
if object.nil? # happens if you try to do an all on empty scope followed by more scopes
object
elsif method.is_a? Array
elsif method.is_a? Array #__secure_remote_access_to_
if method[0] == 'new'
object.new
else
elsif method[0] == 'find_by'
object.send(*method)
else
object.send(:"__secure_remote_access_to_#{method[0]}", object, acting_user, *method[1..-1])
end
elsif method.is_a? String and method[0] == '*'
elsif method.is_a?(String) && method[0] == '*'
object[method.gsub(/^\*/,'').to_i]
else
object.send(method)
object.send(:"__secure_remote_access_to_#{method}", object, acting_user)
end
end
if id and (found.nil? or !(found.class <= model) or (found.id and found.id.to_s != id.to_s))
Expand Down Expand Up @@ -362,13 +364,13 @@ def self.save_records(models, associations, acting_user, validate, save)
id = attributes.delete(model.primary_key) if model.respond_to? :primary_key # if we are saving existing model primary key value will be present
vector = model_to_save[:vector]
vector = [vector[0].constantize] + vector[1..-1].collect do |method|
if method.is_a?(Array) and method.first == "find_by_id"
if method.is_a?(Array) && method.first == "find_by_id"
["find", method.last]
else
method
end
end
reactive_records[model_to_save[:id]] = vectors[vector] = record = find_record(model, id, vector, save) # ??? || validate ???
reactive_records[model_to_save[:id]] = vectors[vector] = record = find_record(model, id, acting_user, vector, save) # ??? || validate ???
next unless record
if attributes.empty?
dont_save_list << record unless save
Expand Down Expand Up @@ -507,7 +509,7 @@ def self.save_records(models, associations, acting_user, validate, save)
if save || validate
{success: false, saved_models: saved_models, message: e}
else
{}
raise e # was returning {} TODO verify that just raising the error at least reports the error
end
end

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,9 @@ class Fetch < Base
]
end
failed do |e|
# AccessViolations are already sent to on_error
Hyperstack.on_error(e, :fetch_error, params.to_h) unless e.is_a? Hyperstack::AccessViolation
e.define_singleton_method(:__hyperstack_on_error) do |operation, params, fmted_message|
Hyperstack.on_error(operation, self, params, fmted_message)
end
raise e
end
end
Expand Down
16 changes: 14 additions & 2 deletions ruby/hyper-model/lib/reactive_record/permissions.rb
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ class << self
def belongs_to(attr_name, *args)
belongs_to_without_reactive_record_add_is_method(attr_name, *args).tap do
define_method "#{attr_name}_is?".to_sym do |model|
self.class.reflections[attr_name].foreign_key == model.id
self.class.reflections[attr_name.to_s].foreign_key == model.id
end
end
end
Expand All @@ -103,7 +103,19 @@ def check_permission_with_acting_user(user, permission, *args)
self.acting_user = old
self
else
Hyperstack::InternalPolicy.raise_operation_access_violation(:crud_access_violation, "for #{self} - #{permission}(#{args}) acting_user: #{user}")
acting_user_string =
if acting_user
id = user.respond_to?(:id) ? user.id : user
"not allowed for acting_user: <##{user.class} id: #{user}>"
else
"not allowed without acting_user (acting_user = nil)"
end

message = "CRUD access violation: <##{self.class} id: #{self.id}> - #{permission}(#{args}) #{acting_user_string}"
if permission == :view_permitted?
details = Hyperstack::PolicyDiagnostics.policy_dump_for(self, user)
end
Hyperstack::InternalPolicy.raise_operation_access_violation(message, details || '')
end
end

Expand Down
36 changes: 28 additions & 8 deletions ruby/hyper-model/lib/reactive_record/server_data_cache.rb
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,26 @@ def initialize(db_cache, acting_user, klass, preloaded_records)
@db_cache.add_item_to_cache self
end

def to_s
acting_user_string =
if acting_user
" - with acting user: <#{acting_user.class.name} id: #{acting_user.id}>"
else
' - with no acting user'
end
vector.collect do |e|
if e.is_a? String
e
elsif e.is_a? Array
e.length > 1 ? "#{e.first}(#{e[1..-1].join(', ')})" : e.first
else
e.name
end
end.join('.') + acting_user_string
rescue
vector.to_s + acting_user_string
end

def start_timing(&block)
ServerDataCache.class.start_timing(&block)
end
Expand All @@ -245,11 +265,11 @@ def apply_method_to_cache(method)
cache_item.apply_star || representative
elsif method == "*all"
# if we secure the collection then we assume its okay to read the ids
secured_value = cache_item.value.__secure_collection_check(@acting_user)
secured_value = cache_item.value.__secure_collection_check(cache_item)
cache_item.build_new_cache_item(timing(:active_record) { secured_value.collect { |record| record.id } }, method, method)
elsif method == "*count"
secured_value = cache_item.value.__secure_collection_check(@acting_user)
cache_item.build_new_cache_item(timing(:active_record) { cache_item.value.__secure_collection_check(@acting_user).count }, method, method)
secured_value = cache_item.value.__secure_collection_check(cache_item)
cache_item.build_new_cache_item(timing(:active_record) { cache_item.value.__secure_collection_check(cache_item).count }, method, method)
elsif preloaded_value = @preloaded_records[cache_item.absolute_vector + [method]]
# no security check needed since we already evaluated this
cache_item.build_new_cache_item(preloaded_value, method, method)
Expand All @@ -273,10 +293,10 @@ def apply_method_to_cache(method)
else
raise "method missing"
end
rescue Exception => e # this check may no longer be needed as we are quite explicit now on which methods we apply
# ReactiveRecord::Pry::rescued(e)
::Rails.logger.debug "\033[0;31;1mERROR: HyperModel exception caught when applying #{method} to db object #{cache_item.value}: #{e}\033[0;30;21m"
raise e, "HyperModel fetching records failed, exception caught when applying #{method} to db object #{cache_item.value}: #{e}", e.backtrace
# rescue Exception => e # this check may no longer be needed as we are quite explicit now on which methods we apply
# # ReactiveRecord::Pry::rescued(e)
# #::Rails.logger.debug "\033[0;31;1mERROR: HyperModel exception caught when applying #{method} to db object #{cache_item.value}: #{e}\033[0;30;21m"
# raise e, "HyperModel fetching records failed, exception caught when applying #{method} to db object #{cache_item.value}: #{e}", e.backtrace
end
end
end
Expand All @@ -296,7 +316,7 @@ def aggregation?(method)
end

def apply_star
if @value && @value.__secure_collection_check(@acting_user) && @value.length > 0
if @value && @value.__secure_collection_check(self) && @value.length > 0
i = -1
@value.inject(nil) do |representative, current_value|
i += 1
Expand Down
Loading

0 comments on commit a222222

Please sign in to comment.