Mike Stamb Mike Stamb - 2 months ago 9
Ruby Question

How can I make it so only users who are admins can post articles?

The user model has a attribute :admin which can be true or false. I want to make a validation so only users with the admin = true can post articles or see the "New article" button in the view.

I am using the devise gem

Controller (articles_controller.rb) :

class ArticlesController < ApplicationController
before_action :set_article, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!

# GET /articles
# GET /articles.json
def index
@articles = Article.paginate(page: params[:page], per_page: 4)
end

# GET /articles/1
# GET /articles/1.json
def show
end

# GET /articles/new
def new
@article = current_user.articles.build
end

# GET /articles/1/edit
def edit
end

# POST /articles
# POST /articles.json
def create
@article = current_user.articles.build(article_params)

respond_to do |format|
if @article.save
format.html { redirect_to @article, notice: 'Article was successfully created.' }
format.json { render :show, status: :created, location: @article }
else
format.html { render :new }
format.json { render json: @article.errors, status: :unprocessable_entity }
end
end
end

# PATCH/PUT /articles/1
# PATCH/PUT /articles/1.json
def update
respond_to do |format|
if @article.update(article_params)
format.html { redirect_to @article, notice: 'Article was successfully updated.' }
format.json { render :show, status: :ok, location: @article }
else
format.html { render :edit }
format.json { render json: @article.errors, status: :unprocessable_entity }
end
end
end

# DELETE /articles/1
# DELETE /articles/1.json
def destroy
@article.destroy
respond_to do |format|
format.html { redirect_to articles_url, notice: 'Article was successfully destroyed.' }
format.json { head :no_content }
end
end

private
# Use callbacks to share common setup or constraints between actions.
def set_article
@article = Article.find(params[:id])
end

# Never trust parameters from the scary internet, only allow the white list through.
def article_params
params.require(:article).permit(:title, :body)
end
end


Model (article.rb):

class Article < ActiveRecord::Base

belongs_to :user
has_many :comments


validates :title, length: { minimum: 5 }
validates :title, uniqueness: true, uniqueness: { message: "This article title has already been posted."}
validates :body, length: { minimum: 15 }
end


Model (user.rb):

class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
validates_uniqueness_of :username
has_many :articles
has_many :comments

end


Schema :

ActiveRecord::Schema.define(version: 20160320222854) do

create_table "articles", force: :cascade do |t|
t.string "title"
t.text "body"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "user_id"
end

create_table "comments", force: :cascade do |t|
t.text "body"
t.integer "user_id"
t.integer "article_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end

add_index "comments", ["article_id"], name: "index_comments_on_article_id"
add_index "comments", ["user_id"], name: "index_comments_on_user_id"

create_table "contacts", force: :cascade do |t|
t.string "name"
t.string "email"
t.text "message"
t.datetime "created_at"
t.datetime "updated_at"
end

create_table "users", force: :cascade do |t|
t.string "username", default: "", null: false
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.integer "sign_in_count", default: 0, null: false
t.datetime "current_sign_in_at"
t.datetime "last_sign_in_at"
t.string "current_sign_in_ip"
t.string "last_sign_in_ip"
t.string "confirmation_token"
t.datetime "confirmed_at"
t.datetime "confirmation_sent_at"
t.string "unconfirmed_email"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.boolean "admin", default: false
t.string "firstname"
t.string "lastname"
end

add_index "users", ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true
add_index "users", ["email"], name: "index_users_on_email", unique: true
add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true

end


view /articles (index.html.erb):

<div class="row">
<!-- Blog article Content Column -->
<div class="col-lg-8">
<% @articles.each do |article| %>
<!-- Blog article -->

<!-- Title -->
<h4 style="font-size: 45.5px;"><%= link_to article.title, article %></h4>
<!-- Date/Time -->
<p><span class="glyphicon glyphicon-time"></span>
<%= time_ago_in_words(article.created_at) %>
</p>


<!-- Author -->
<p>
Article By:<strong> <%= article.user.username %></strong>
</p>

<hr>

<% end %>

<%= link_to "New article", new_article_path, class: "btn btn-default btn-xs" %>

</div>



<!-- paginate -->

<%= will_paginate @articles %>

<br />


Thanks in advance to all the wonderful people here willing to lend a helping hand.

Answer

You can add a filter method in Application Controller

def admin_access
  render(text: 'Unauthorised') and return unless current_user.admin
end

And use this in Atricles controller or any other place where it is required

before_filter :admin_access, only: [:new, :create, :edit, :update, :destroy]

And in the views, check if user is admin

<% if current_user.admin %>
  # new/edit/delete links
<% end %>

Even though, not showing the link in the view solves the problem, It is always better to have proper authorisation logic in the server side.

Comments