Photogram Authorization
Tags: webdev, ruby, rails, authorization
Published: 2024-03-11 20:11:05

➔ 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]
```
<hr>
- 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.

<hr>
- 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.
<hr>
- 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”.
<hr>

- 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.