X-Git-Url: https://git.arvados.org/arvados.git/blobdiff_plain/2ab2ec271e09c54f6e7e1d4c3adbd1d76e56f1cd..797885e26f85f04123bcba751addcd415ed3db2e:/services/api/app/models/arvados_model.rb diff --git a/services/api/app/models/arvados_model.rb b/services/api/app/models/arvados_model.rb index 74d1d95076..339bc9e23f 100644 --- a/services/api/app/models/arvados_model.rb +++ b/services/api/app/models/arvados_model.rb @@ -8,7 +8,7 @@ require 'record_filters' require 'serializers' require 'request_error' -class ArvadosModel < ActiveRecord::Base +class ArvadosModel < ApplicationRecord self.abstract_class = true include ArvadosModelUpdates @@ -41,6 +41,16 @@ class ArvadosModel < ActiveRecord::Base class_name: 'Link', primary_key: :uuid) + # If async is true at create or update, permission graph + # update is deferred allowing making multiple calls without the performance + # penalty. + attr_accessor :async_permissions_update + + # Ignore listed attributes on mass assignments + def self.protected_attributes + [] + end + class PermissionDeniedError < RequestError def http_status 403 @@ -92,7 +102,11 @@ class ArvadosModel < ActiveRecord::Base # The following permit! is necessary even with # "ActionController::Parameters.permit_all_parameters = true", # because permit_all does not permit nested attributes. + raw_params ||= {} + if raw_params + raw_params = raw_params.to_hash + raw_params.delete_if { |k, _| self.protected_attributes.include? k } serialized_attributes.each do |colname, coder| param = raw_params[colname.to_sym] if param.nil? @@ -103,6 +117,15 @@ class ArvadosModel < ActiveRecord::Base raise ArgumentError.new("#{colname} parameter cannot have non-string hash keys") end end + # Check JSONB columns that aren't listed on serialized_attributes + columns.select{|c| c.type == :jsonb}.collect{|j| j.name}.each do |colname| + if serialized_attributes.include? colname || raw_params[colname.to_sym].nil? + next + end + if has_nonstring_keys?(raw_params[colname.to_sym]) + raise ArgumentError.new("#{colname} parameter cannot have non-string hash keys") + end + end end ActionController::Parameters.new(raw_params).permit! end @@ -274,9 +297,8 @@ class ArvadosModel < ActiveRecord::Base if !include_trash if sql_table != "api_client_authorizations" # Only include records where the owner is not trashed - sql_conds = "NOT EXISTS(SELECT 1 FROM #{PERMISSION_VIEW} "+ - "WHERE trashed = 1 AND "+ - "(#{sql_table}.owner_uuid = target_uuid)) #{exclude_trashed_records}" + sql_conds = "#{sql_table}.owner_uuid NOT IN (SELECT target_uuid FROM #{PERMISSION_VIEW} "+ + "WHERE trashed = 1) #{exclude_trashed_records}" end end else @@ -294,14 +316,14 @@ class ArvadosModel < ActiveRecord::Base # see issue 13208 for details. # Match a direct read permission link from the user to the record uuid - direct_check = "EXISTS(SELECT 1 FROM #{PERMISSION_VIEW} "+ - "WHERE user_uuid IN (:user_uuids) AND perm_level >= 1 #{trashed_check} AND target_uuid = #{sql_table}.uuid)" + direct_check = "#{sql_table}.uuid IN (SELECT target_uuid FROM #{PERMISSION_VIEW} "+ + "WHERE user_uuid IN (:user_uuids) AND perm_level >= 1 #{trashed_check})" # Match a read permission link from the user to the record's owner_uuid owner_check = "" if sql_table != "api_client_authorizations" and sql_table != "groups" then - owner_check = "OR EXISTS(SELECT 1 FROM #{PERMISSION_VIEW} "+ - "WHERE user_uuid IN (:user_uuids) AND perm_level >= 1 #{trashed_check} AND target_uuid = #{sql_table}.owner_uuid AND target_owner_uuid IS NOT NULL) " + owner_check = "OR #{sql_table}.owner_uuid IN (SELECT target_uuid FROM #{PERMISSION_VIEW} "+ + "WHERE user_uuid IN (:user_uuids) AND perm_level >= 1 #{trashed_check} AND target_owner_uuid IS NOT NULL) " end links_cond = "" @@ -353,7 +375,7 @@ class ArvadosModel < ActiveRecord::Base # discover a unique name. It is necessary to handle name choosing at # this level (as opposed to the client) to ensure that record creation # never fails due to a race condition. - err = rn.original_exception + err = rn.cause raise unless err.is_a?(PG::UniqueViolation) # Unfortunately ActiveRecord doesn't abstract out any of the @@ -389,7 +411,7 @@ class ArvadosModel < ActiveRecord::Base end def logged_attributes - attributes.except(*Rails.configuration.unlogged_attributes) + attributes.except(*Rails.configuration.AuditLogs.UnloggedAttributes) end def self.full_text_searchable_columns @@ -400,10 +422,11 @@ class ArvadosModel < ActiveRecord::Base def self.full_text_tsvector parts = full_text_searchable_columns.collect do |column| - cast = serialized_attributes[column] ? '::text' : '' + is_jsonb = self.columns.select{|x|x.name == column}[0].type == :jsonb + cast = (is_jsonb || serialized_attributes[column]) ? '::text' : '' "coalesce(#{column}#{cast},'')" end - "to_tsvector('english', substr(#{parts.join(" || ' ' || ")}, 0, 1000000))" + "to_tsvector('english', substr(#{parts.join(" || ' ' || ")}, 0, 8000))" end def self.apply_filters query, filters @@ -445,7 +468,7 @@ class ArvadosModel < ActiveRecord::Base end rescue ActiveRecord::RecordNotFound => e errors.add :owner_uuid, "is not owned by any user: #{e}" - return false + throw(:abort) end if uuid_in_path[x] if x == owner_uuid @@ -453,7 +476,7 @@ class ArvadosModel < ActiveRecord::Base else errors.add :owner_uuid, "has an ownership cycle" end - return false + throw(:abort) end uuid_in_path[x] = true end @@ -558,6 +581,8 @@ class ArvadosModel < ActiveRecord::Base self.owner_uuid ||= current_default_owner if self.respond_to? :owner_uuid= if !anonymous_updater self.modified_by_user_uuid = current_user ? current_user.uuid : nil + end + if !timeless_updater self.modified_at = current_time end self.modified_by_client_uuid = current_api_client ? current_api_client.uuid : nil @@ -659,7 +684,8 @@ class ArvadosModel < ActiveRecord::Base # we'll convert symbols to strings when loading from the # database. (Otherwise, loading and saving an object with existing # symbols in a serialized field will crash.) - self.class.serialized_attributes.each do |colname, attr| + jsonb_cols = self.class.columns.select{|c| c.type == :jsonb}.collect{|j| j.name} + (jsonb_cols + self.class.serialized_attributes.keys).uniq.each do |colname| if self.class.has_symbols? attributes[colname] attributes[colname] = self.class.recursive_stringify attributes[colname] send(colname + '=', @@ -709,7 +735,7 @@ class ArvadosModel < ActiveRecord::Base end def self.uuid_like_pattern - "#{Rails.configuration.uuid_prefix}-#{uuid_prefix}-_______________" + "#{Rails.configuration.ClusterID}-#{uuid_prefix}-_______________" end def self.uuid_regex @@ -788,8 +814,8 @@ class ArvadosModel < ActiveRecord::Base end def is_audit_logging_enabled? - return !(Rails.configuration.max_audit_log_age.to_i == 0 && - Rails.configuration.max_audit_log_delete_batch.to_i > 0) + return !(Rails.configuration.AuditLogs.MaxAge.to_i == 0 && + Rails.configuration.AuditLogs.MaxDeleteBatch.to_i > 0) end def log_start_state