Module: CanCanCan::AbstractResourceController

Extended by:
ActiveSupport::Concern
Defined in:
lib/cancancan/abstract_resource_controller.rb

Constant Summary collapse

MAX_ASSOCIATIVE_NESTED_DEPTH =

Used to stop infinite recursive on associations (could just be deeply nested structures. Could also be self-referencing).

60
REGEX_FOR_HTML_TAG_DETECTION =
/.*\<\/?[^_\W]+\>.*/
IDS_ATTIB_PERMISSION_KEY_GEN =

to handle adding/removing associations by “_ids” suffix

Proc.new { |assoc_key| "#{assoc_key.to_s.singularize}_ids".to_sym }
IDS_ACTION_PERMISSION_KEY_GEN =
Proc.new { |assoc_key| "_can_add_or_remove_association_#{assoc_key.to_s}".to_sym }
NESTED_ATTIB_PERMISSION_KEY_GEN =

to handle updating nested attributes

Proc.new { |assoc_key| "#{assoc_key.to_s}_attributes".to_sym }
NESTED_ACTION_PERMISSION_KEY_GEN =
Proc.new { |assoc_key| "_can_update_association_#{assoc_key.to_s}".to_sym }
DEFAULT_PARAMETER_SANITIZER_ALLOWED_TAGS =

probably a better way to do this. If there is, it’s poorly documented.

DEFAULT_PARAMETER_SANITIZER_ALLOWED_TAGS - Add to this env var any values to also allow for HTML tags (i.e.: label,span,text_area) DEFAULT_PARAMETER_SANITIZER_ALLOWED_ATTRIBS - Add to this env var any values to also allow for HTML attribs (i.e.: ng-show,ng-hide,data-id)

(
    %w[
    p
    div
    span
    body
    b
    strong
    br
    center
    font
    label
    pre
    tr
    td
    table
    text_area
    ul
    li
    footer
    em
    ol
    i
    select
    option
  ] + (ENV['DEFAULT_PARAMETER_SANITIZER_ALLOWED_TAGS']&.split(',')&.collect(&:strip) || [])
).freeze
DEFAULT_PARAMETER_SANITIZER_ALLOWED_ATTRIBS =

Only allow attribs that are allowed in HTML friendly text blocks

  • i.e. NO HREFs!

(
    %w[
    style
    id
    class
    type
    value
  ] + (ENV['DEFAULT_PARAMETER_SANITIZER_ALLOWED_ATTRIBS']&.split(',')&.collect(&:strip) || [])
).freeze

Instance Method Summary collapse

Instance Method Details

#createObject



126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
# File 'lib/cancancan/abstract_resource_controller.rb', line 126

def create
  authorize! :create, @resource_class
  # @resource = @resource_class.new(resource_params)

  # This 2nd @resource initiation is so we run run whitelisting attribs on the object.
  #   Class whitelisting is far more broad than object attrib whitelisting.
  #   Necessary if class permissions have a permissions-block.
  @resource ||= @resource_class.new(resource_params(@resource))

  # 2nd auth on the object itself
  authorize! :create, @resource

  if @resource.save
    respond_with_resource
  else
    begin
      Rails.logger.warn "Failed object validations: could not create #{@resource_class}, id: #{@resource.id}: #{@resource.errors.full_messages}"
      respond_with_resource_invalid
    rescue Exception => e
      Rails.logger.error "CanCanCanResourceController - Caught Internal Server Error: " + e.class.to_s + ': ' + e.message
      Rails.logger.error Rails.backtrace_cleaner.clean(e.backtrace).join("\n").to_s
      respond_with_resource_error
    end
  end
end

#destroyObject



187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
# File 'lib/cancancan/abstract_resource_controller.rb', line 187

def destroy
  authorize! :destroy, @resource_class
  @resource ||= @resource_class.find(params[:id])
  authorize! :destroy, @resource
  # retuning the resource in a pre-destroyed state as a destroy response
  results = @resource
  if @resource.destroy
    respond_after_destroy
  else
    begin
      Rails.logger.warn "Failed object validations: could not destroy #{@resource_class}, id: #{@resource.id}: #{@resource.errors.full_messages}"
      respond_with_resource_invalid
    rescue Exception => e
      Rails.logger.error "CanCanCanResourceController - Caught Internal Server Error: " + e.class.to_s + ': ' + e.message
      Rails.logger.error Rails.backtrace_cleaner.clean(e.backtrace).join("\n").to_s
      respond_with_resource_error
    end
  end
end

#editObject



112
113
114
115
116
117
118
119
120
121
122
123
124
# File 'lib/cancancan/abstract_resource_controller.rb', line 112

def edit
  authorize! :update, @resource_class
  @resource ||= @resource_class.find(params[:id])
  authorize! :update, @resource

  respond_to do |format|
    format.html # Renders the default
    format.json { render json: @resources }
    format.xml { render xml: @resources }
    format.csv # Renders the default
    format.xlsx # Renders the default
  end
end

#indexObject



75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/cancancan/abstract_resource_controller.rb', line 75

def index
  authorize! :index, @resource_class
  @resources ||= @resource_class

  begin
    @resources = @resources.accessible_by(current_ability)
  rescue CanCan::Error => e
    # The accessible_by call cannot be used with a block 'can' definition
    # Need to switch over to SQL permissions, not using the blocks
    Rails.logger.error "Error: resource class, #{@resource_class.name}, is using CanCan block definitions, not SQL permissions. Unable to run index permission filter"
    raise e
  end

  @resources = index_resource_query(@resources)

  respond_with_resources
end

#newObject



102
103
104
105
106
107
108
109
110
# File 'lib/cancancan/abstract_resource_controller.rb', line 102

def new
  authorize! :create, @resource_class
  @resource ||= @resource_class.new(resource_params)
  # 2nd auth on the object itself
  #   Not authing on the nested resources, that could have come in as nested attributes.
  authorize! :create, @resource

  respond_with_resource
end

#showObject



93
94
95
96
97
98
99
100
# File 'lib/cancancan/abstract_resource_controller.rb', line 93

def show
  authorize! :show, @resource_class
  # Allow @resource to be set from subclass controller
  @resource ||= @resource_class.find(params[:id])
  authorize! :show, @resource
  
  respond_with_resource
end

#updateObject



152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
# File 'lib/cancancan/abstract_resource_controller.rb', line 152

def update
  authorize! :update, @resource_class
  @resource ||= @resource_class.find(params[:id])
  authorize! :update, @resource

  second_authorize = false
  ActiveRecord::Base.transaction do
    @resource.assign_attributes(resource_params(@resource))
    second_authorize = can?(action_name.to_sym, @resource)
    unless second_authorize
      # NOTE: Does not halt the controller process, just rolls back the DB
      raise ActiveRecord::Rollback
    end
  end

  unless second_authorize
    raise CanCan::AccessDenied.new("Not authorized!", action_name.to_sym, @resource)
  end

  # 2nd auth, on the updates of the object, without saving, so we can rollback without auth.
  # authorize! :update, @resource
  if @resource.save
    respond_with_resource
  else
    begin
      Rails.logger.warn "Failed object validations: could not update #{@resource_class}, id: #{@resource.id}: #{@resource.errors.full_messages}"
      respond_with_resource_error
    rescue Exception => e
      Rails.logger.error "CanCanCanResourceController - Caught Internal Server Error: " + e.class.to_s + ': ' + e.message
      Rails.logger.error Rails.backtrace_cleaner.clean(e.backtrace).join("\n").to_s
      respond_with_resource_error
    end
  end
end