DEV Community

Mary Webby
Mary Webby

Posted on

Photogram Authorization

➔ 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 in except and only parameters to our resources :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]
Enter fullscreen mode Exit fullscreen mode

  • 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
Enter fullscreen mode Exit fullscreen mode
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 
Enter fullscreen mode Exit fullscreen mode

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 %>
Enter fullscreen mode Exit fullscreen mode

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 %>
Enter fullscreen mode Exit fullscreen mode

@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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

We could test that, but we would unfortunately end up getting a nil 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
Enter fullscreen mode Exit fullscreen mode

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)