➔ Notes
- first begin with checking for traceable and editable routes that our users could use to change info that isn't theirs. if found, we will then need to go into our
routes.rb
and add inexcept
andonly
parameters to ourresources :follow_requests
controller to not let other users be able to change who gets to follow and unfollow who. so after for photogram it would look something like this
resources :comments
resources :follow_requests, except: [:index, :show, :new, :edit]
resources :likes, only: [:create, :destroy]
resources :photos, except: [:index]
- next we can move onto the controller with
before_action
in private, this action will allow us to add some blockers to specific routes so users can't just add and delete whichever photo they want to.
class PhotosController < ApplicationController
before_action :set_photo, only: %i[ show edit update destroy ]
before_action :ensure_current_user_is_owner, only: [:destroy, :update, :edit]
# ...
end
def ensure_current_user_is_owner
if current_user != @photo.owner
redirect_back fallback_location: root_url, alert: "You're not authorized for that."
end
To reiterate:
- First we ask what routes we actually want and filter them from the routes.rb with except or only. For the remaining routes, we ask who is allowed to do what on each route.
- we use conditionals for keeping edit and delete buttons cause users shouldn’t see the edit or delete links. For instance, we would put a conditional within the photo.html.erb in the
app/views/photos/_photo.html.erb
.
<% if current_user == photo.owner %>
<%= link_to edit_photo_path(photo), class: "btn btn-link btn-sm text-muted" do %>
<i class="fas fa-edit fa-fw"></i>
<% end %>
<%= link_to photo, method: :delete, class: "btn btn-link btn-sm text-muted" do %>
<i class="fas fa-trash fa-fw"></i>
<% end %>
<% end %>
We are checking that the current_user is the photo.owner in the view template, and only rendering the Font Awesome links if they are.
- hiding if users are private would require another conditional in the
app/views/users/show.html.erb
file, this fix to the code would allow us to not see users if they are private, but to see them if we follow them
<% if current_user == @user || !@user.private? || current_user.leaders.include?(@user) %>
<div class="row mb-2">
<div class="col-md-6 offset-md-3">
<%= render "users/profile_nav", user: @user %>
</div>
</div>
<% @user.own_photos.each do |photo| %>
<div class="row mb-4">
<div class="col-md-6 offset-md-3">
<%= render "photos/photo", photo: photo %>
</div>
</div>
<% end %>
<% end %>
@user.private?
is going to be true or false. The preceding ! asks “if not”, flipping the true or false. In other words saying, “if the user is not private then render the page”.
- now we have to make it so users are only able to comment on photos in our app if they are following the user or the user is public, so in the actions of the
app/controllers/comments_controller.rb
. we may have hid the route and/or content, but we also want to bounce them from actions they aren't allowed in.
class CommentsController < ApplicationController
before_action :set_comment, only: %i[ show edit update destroy ]
before_action :is_an_authorized_user, only: [:destroy, :create]
# ...
def is_an_authorized_user
if current_user == @comment.owner || !@comment.owner.private? || current_user.leaders.include?(@comment.owner)
redirect_back fallback_location: root_url, alert: "Not authorized"
end
end
# ...
end
That copy-paste of the conditional won’t work, because we don’t have @user
here, we have @comment
. We need to get from @comment
to the owner of it, turns out we’ll need to add another association accessor first. so, we need to go into the comment model, and make sure we are getting the user of the photo through owner.
class Comment < ApplicationRecord
belongs_to :author, class_name: "User", counter_cache: true
belongs_to :photo, counter_cache: true
has_one :owner, through: :photo
validates :body, presence: true
end
We could test that, but we would unfortunately end up getting a ni
l returned for @comment
when we try to create or destroy. Why? Because the before_action :set_comment
is not being run before our new is_authorized_user
method (the new method is not in the only
list for :set_comment
). Actually if we tried debugging this, we would realize a flaw in our logib up until this point, we dont want to be looking at the owner of the comment, we want to be looking at the owner of the photo of which we are commenting on, so we instead would be changing our code from before, to this
class CommentsController < ApplicationController
before_action :set_comment, only: %i[ show edit update destroy ]
before_action :is_an_authorized_user, only: [:destroy, :create]
# ...
def is_an_authorized_user
@photo = Photo.find(params.fetch(:comment).fetch(:photo_id))
if current_user != @photo.owner && @photo.owner.private? && !current_user.leaders.include?(@photo.owner)
redirect_back fallback_location: root_url, alert: "Not authorized"
end
end
# ...
end
first, we will be needing to get the current photo from the params
hash (from comment[photo_id]
), then we need to change the logic in our conditional (switching the location of the !
“if not” modifiers, and changing ||
to &&
) to get what we want: Users can only comment of public photos, or photos of their leaders.
Top comments (0)