diff --git a/Gemfile.lock b/Gemfile.lock index aa2ddef9..6a3797d2 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,80 +1,81 @@ PATH remote: . specs: - motor-admin (0.4.24) + motor-admin (0.4.27) ar_lazy_preload (~> 1.0) audited (~> 5.0) cancancan (~> 3.0) + csv (>= 3.0) fugit (~> 1.0) rails (>= 5.2) GEM remote: https://rubygems.org/ specs: - actioncable (7.0.4) - actionpack (= 7.0.4) - activesupport (= 7.0.4) + actioncable (7.0.8.3) + actionpack (= 7.0.8.3) + activesupport (= 7.0.8.3) nio4r (~> 2.0) websocket-driver (>= 0.6.1) - actionmailbox (7.0.4) - actionpack (= 7.0.4) - activejob (= 7.0.4) - activerecord (= 7.0.4) - activestorage (= 7.0.4) - activesupport (= 7.0.4) + actionmailbox (7.0.8.3) + actionpack (= 7.0.8.3) + activejob (= 7.0.8.3) + activerecord (= 7.0.8.3) + activestorage (= 7.0.8.3) + activesupport (= 7.0.8.3) mail (>= 2.7.1) net-imap net-pop net-smtp - actionmailer (7.0.4) - actionpack (= 7.0.4) - actionview (= 7.0.4) - activejob (= 7.0.4) - activesupport (= 7.0.4) + actionmailer (7.0.8.3) + actionpack (= 7.0.8.3) + actionview (= 7.0.8.3) + activejob (= 7.0.8.3) + activesupport (= 7.0.8.3) mail (~> 2.5, >= 2.5.4) net-imap net-pop net-smtp rails-dom-testing (~> 2.0) - actionpack (7.0.4) - actionview (= 7.0.4) - activesupport (= 7.0.4) - rack (~> 2.0, >= 2.2.0) + actionpack (7.0.8.3) + actionview (= 7.0.8.3) + activesupport (= 7.0.8.3) + rack (~> 2.0, >= 2.2.4) rack-test (>= 0.6.3) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.0, >= 1.2.0) - actiontext (7.0.4) - actionpack (= 7.0.4) - activerecord (= 7.0.4) - activestorage (= 7.0.4) - activesupport (= 7.0.4) + actiontext (7.0.8.3) + actionpack (= 7.0.8.3) + activerecord (= 7.0.8.3) + activestorage (= 7.0.8.3) + activesupport (= 7.0.8.3) globalid (>= 0.6.0) nokogiri (>= 1.8.5) - actionview (7.0.4) - activesupport (= 7.0.4) + actionview (7.0.8.3) + activesupport (= 7.0.8.3) builder (~> 3.1) erubi (~> 1.4) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.1, >= 1.2.0) - activejob (7.0.4) - activesupport (= 7.0.4) + activejob (7.0.8.3) + activesupport (= 7.0.8.3) globalid (>= 0.3.6) - activemodel (7.0.4) - activesupport (= 7.0.4) - activerecord (7.0.4) - activemodel (= 7.0.4) - activesupport (= 7.0.4) + activemodel (7.0.8.3) + activesupport (= 7.0.8.3) + activerecord (7.0.8.3) + activemodel (= 7.0.8.3) + activesupport (= 7.0.8.3) activerecord-sqlserver-adapter (7.0.0.0) activerecord (~> 7.0.0) tiny_tds - activestorage (7.0.4) - actionpack (= 7.0.4) - activejob (= 7.0.4) - activerecord (= 7.0.4) - activesupport (= 7.0.4) + activestorage (7.0.8.3) + actionpack (= 7.0.8.3) + activejob (= 7.0.8.3) + activerecord (= 7.0.8.3) + activesupport (= 7.0.8.3) marcel (~> 1.0) mini_mime (>= 1.1.0) - activesupport (7.0.4) + activesupport (7.0.8.3) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (>= 1.6, < 2) minitest (>= 5.1) @@ -84,9 +85,9 @@ GEM ar_lazy_preload (1.1.2) rails (>= 5.2) ast (2.4.2) - audited (5.4.2) - activerecord (>= 5.0, < 7.2) - request_store (~> 1.2) + audited (5.6.0) + activerecord (>= 5.2, < 7.2) + activesupport (>= 5.2, < 7.2) aws-eventstream (1.2.0) aws-partitions (1.684.0) aws-sdk-core (3.168.4) @@ -108,7 +109,7 @@ GEM msgpack (~> 1.2) brakeman (5.4.0) builder (3.2.4) - cancancan (3.5.0) + cancancan (3.6.1) capybara (3.38.0) addressable matrix @@ -119,14 +120,15 @@ GEM regexp_parser (>= 1.5, < 3.0) xpath (~> 3.2) coderay (1.1.3) - concurrent-ruby (1.1.10) + concurrent-ruby (1.3.1) crack (0.4.5) rexml crass (1.0.6) + csv (3.3.0) cuprite (0.14.3) capybara (~> 3.0) ferrum (~> 0.13.0) - date (3.3.3) + date (3.3.4) devise (4.8.1) bcrypt (~> 3.0) orm_adapter (~> 0.1) @@ -136,7 +138,7 @@ GEM diff-lcs (1.5.0) docile (1.4.0) erubi (1.12.0) - et-orbi (1.2.7) + et-orbi (1.2.11) tzinfo factory_bot (6.2.1) activesupport (>= 5.0.0) @@ -151,13 +153,13 @@ GEM webrick (~> 1.7) websocket-driver (>= 0.6, < 0.8) ffi (1.15.5) - fugit (1.9.0) - et-orbi (~> 1, >= 1.2.7) + fugit (1.11.0) + et-orbi (~> 1, >= 1.2.11) raabro (~> 1.4) - globalid (1.0.0) - activesupport (>= 5.0) + globalid (1.2.1) + activesupport (>= 6.1) hashdiff (1.0.1) - i18n (1.12.0) + i18n (1.14.5) concurrent-ruby (~> 1.0) image_processing (1.12.2) mini_magick (>= 4.9.5, < 5) @@ -168,35 +170,38 @@ GEM addressable (~> 2.7) letter_opener (1.8.1) launchy (>= 2.2, < 3) - loofah (2.19.1) + loofah (2.22.0) crass (~> 1.0.2) - nokogiri (>= 1.5.9) - mail (2.8.0) + nokogiri (>= 1.12.0) + mail (2.8.1) mini_mime (>= 0.1.1) net-imap net-pop net-smtp - marcel (1.0.2) + marcel (1.0.4) matrix (0.4.2) - method_source (1.0.0) + method_source (1.1.0) mini_magick (4.12.0) - mini_mime (1.1.2) - mini_portile2 (2.8.1) - minitest (5.16.3) + mini_mime (1.1.5) + mini_portile2 (2.8.6) + minitest (5.23.1) msgpack (1.6.0) - mysql2 (0.5.4) - net-imap (0.3.4) + mysql2 (0.5.6) + net-imap (0.4.11) date net-protocol net-pop (0.1.2) net-protocol - net-protocol (0.2.1) + net-protocol (0.2.2) timeout - net-smtp (0.3.3) + net-smtp (0.5.0) net-protocol - nio4r (2.5.8) - nokogiri (1.13.10) - mini_portile2 (~> 2.8.0) + nio4r (2.7.3) + nokogiri (1.16.5-arm64-darwin) + racc (~> 1.4) + nokogiri (1.16.5-x86_64-darwin) + racc (~> 1.4) + nokogiri (1.16.5-x86_64-linux) racc (~> 1.4) oj (3.13.23) orm_adapter (0.5.0) @@ -213,41 +218,41 @@ GEM puma (6.0.1) nio4r (~> 2.0) raabro (1.4.0) - racc (1.6.2) - rack (2.2.4) - rack-test (2.0.2) + racc (1.8.0) + rack (2.2.9) + rack-test (2.1.0) rack (>= 1.3) - rails (7.0.4) - actioncable (= 7.0.4) - actionmailbox (= 7.0.4) - actionmailer (= 7.0.4) - actionpack (= 7.0.4) - actiontext (= 7.0.4) - actionview (= 7.0.4) - activejob (= 7.0.4) - activemodel (= 7.0.4) - activerecord (= 7.0.4) - activestorage (= 7.0.4) - activesupport (= 7.0.4) + rails (7.0.8.3) + actioncable (= 7.0.8.3) + actionmailbox (= 7.0.8.3) + actionmailer (= 7.0.8.3) + actionpack (= 7.0.8.3) + actiontext (= 7.0.8.3) + actionview (= 7.0.8.3) + activejob (= 7.0.8.3) + activemodel (= 7.0.8.3) + activerecord (= 7.0.8.3) + activestorage (= 7.0.8.3) + activesupport (= 7.0.8.3) bundler (>= 1.15.0) - railties (= 7.0.4) - rails-dom-testing (2.0.3) - activesupport (>= 4.2.0) + railties (= 7.0.8.3) + rails-dom-testing (2.2.0) + activesupport (>= 5.0.0) + minitest nokogiri (>= 1.6) - rails-html-sanitizer (1.4.4) - loofah (~> 2.19, >= 2.19.1) - railties (7.0.4) - actionpack (= 7.0.4) - activesupport (= 7.0.4) + rails-html-sanitizer (1.6.0) + loofah (~> 2.21) + nokogiri (~> 1.14) + railties (7.0.8.3) + actionpack (= 7.0.8.3) + activesupport (= 7.0.8.3) method_source rake (>= 12.2) thor (~> 1.0) zeitwerk (~> 2.5) rainbow (3.1.1) - rake (13.0.6) + rake (13.2.1) regexp_parser (2.6.1) - request_store (1.5.1) - rack (>= 1.4) responders (3.0.1) actionpack (>= 5.0) railties (>= 5.0) @@ -301,10 +306,10 @@ GEM simplecov_json_formatter (0.1.4) sqlite3 (1.5.4) mini_portile2 (~> 2.8.0) - thor (1.2.1) - timeout (0.3.1) + thor (1.3.1) + timeout (0.4.1) tiny_tds (2.1.5) - tzinfo (2.0.5) + tzinfo (2.0.6) concurrent-ruby (~> 1.0) unicode-display_width (2.3.0) warden (1.2.9) @@ -314,18 +319,19 @@ GEM crack (>= 0.3.2) hashdiff (>= 0.4.0, < 2.0.0) webrick (1.7.0) - websocket-driver (0.7.5) + websocket-driver (0.7.6) websocket-extensions (>= 0.1.0) websocket-extensions (0.1.5) xpath (3.2.0) nokogiri (~> 1.8) yard (0.9.28) webrick (~> 1.7.0) - zeitwerk (2.6.6) + zeitwerk (2.6.15) PLATFORMS arm64-darwin-21 arm64-darwin-22 + arm64-darwin-23 x86_64-darwin-20 x86_64-darwin-22 x86_64-linux diff --git a/app/controllers/motor/run_queries_controller.rb b/app/controllers/motor/run_queries_controller.rb index c2a58e34..2db8e104 100644 --- a/app/controllers/motor/run_queries_controller.rb +++ b/app/controllers/motor/run_queries_controller.rb @@ -20,6 +20,8 @@ def create private def render_result + authorize_queries!(@query) + query_result = Queries::RunQuery.call(@query, variables_hash: variables_params.to_unsafe_h, limit: params[:limit].presence, filters: filter_params) @@ -31,6 +33,12 @@ def render_result end end + def authorize_queries!(query) + query.sql_body.to_s.scan(/query_\d+/).each do |name| + Motor::Query.accessible_by(current_ability).find(name.split('_').last) + end + end + def current_user_variables return {} unless current_user diff --git a/lib/motor/queries/run_query.rb b/lib/motor/queries/run_query.rb index 10515a4b..801e2689 100644 --- a/lib/motor/queries/run_query.rb +++ b/lib/motor/queries/run_query.rb @@ -15,6 +15,7 @@ module RunQuery RESERVED_VARIABLES = %w[current_user_id current_user_email].freeze DATABASE_URL_VARIABLE_SUFFIX = '_database_url' + QUERY_VARIABLE_PREFIX = 'query_' DB_LINK_VALIDATE_REGEXP = /(.*?)\s*\{\{\s*\w+_database_url\s*\}\}/i.freeze @@ -188,6 +189,7 @@ def build_statement_attributes(variables) variables.map do |variable_name, value| [value].flatten.map do |val| val = fetch_variable_database_url(variable_name) if variable_name.ends_with?(DATABASE_URL_VARIABLE_SUFFIX) + val = fetch_query_data(variable_name) if variable_name.starts_with?(QUERY_VARIABLE_PREFIX) ActiveRecord::Relation::QueryAttribute.new( variable_name, @@ -206,6 +208,15 @@ def fetch_variable_database_url(variable_name) raise UnknownDatabase, "#{class_name} database is not defined" end + def fetch_query_data(variable_name) + query = Motor::Query.find(variable_name.split('_').last) + + result = Motor::Queries::RunQuery.call(query) + columns = result.columns.pluck(:name) + + result.data.map { |row| columns.zip(row).to_h }.to_json + end + # @param array [Array] # @return [Array] def normalize_statement_for_sql(statement) diff --git a/lib/motor/version.rb b/lib/motor/version.rb index 5bb90d88..511cc2a8 100644 --- a/lib/motor/version.rb +++ b/lib/motor/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module Motor - VERSION = '0.4.26' + VERSION = '0.4.27' end diff --git a/motor-admin.gemspec b/motor-admin.gemspec index 64ae413e..c356698c 100644 --- a/motor-admin.gemspec +++ b/motor-admin.gemspec @@ -36,6 +36,7 @@ Gem::Specification.new do |spec| spec.add_dependency 'cancancan', '~> 3.0' spec.add_dependency 'fugit', '~> 1.0' spec.add_dependency 'rails', '>= 5.2' + spec.add_dependency 'csv', '>= 3.0' spec.post_install_message = " ================== diff --git a/ui/src/queries/pages/show.vue b/ui/src/queries/pages/show.vue index d5291fb9..a9a08b80 100644 --- a/ui/src/queries/pages/show.vue +++ b/ui/src/queries/pages/show.vue @@ -601,7 +601,7 @@ export default { }) variables.forEach((variableName) => { - if (!RESERVED_VARIABLES.includes(variableName) && !variableName.match(/_database_url$/)) { + if (!RESERVED_VARIABLES.includes(variableName) && !variableName.match(/_database_url$/) && !variableName.match(/^query_/)) { const variable = { name: variableName, display_name: titleize(variableName),