Advertisement
  1. Code
  2. Ruby on Rails

Otorisasi dengan Pundit

Scroll to top
Read Time: 8 min

() translation by (you can also view the original English article)

Pundit adalah sebuah alat yang mengizinkanmu melarang bagian tertentu dari aplikasi Rails mu untuk otorisasi pengguna. Caranya dengan menyediakan beberapa pembantu padamu.

Dalam tutorial ini, kamu akan membuat sebuah blog yang melarang beberapa bagian seperti membuat, memperbaharui dan menghapus artikel kecuali oleh pengguna yang terotorisasi.

Memulai

Pertama dimulai dengan membuat sebuah aplikasi Rails baru.

1
rails new pundit-blog -T

Tanda -T memberi tahu Rails untuk membuat aplikasi baru tanpa test suite bawaan. Menjalankan perintah tersebut akan membuat aplikasi Railsmu dan memasan gems bawaan.

Lalu, tambahkan gems berikut ke gemfile mu. Kamu akan menggunakan bootsrap-sass untuk layout aplikasimu, dan Devise akan mengurus otentikasi pengguna.

1
#Gemfile
2
3
...
4
gem 'bootstrap-sass'
5
gem 'devise'

Jalankan perintah untuk memasang gem

1
bundle install

Sekarang ganti nama app/assets/stylesheets/application.css menjadi app/assets/stylesheets/application.scs. Dan tambahkan baris berikut ke bootstrap import.

1
#app/assets/stylesheets/application.scss
2
3
...
4
@import 'bootstrap-sprockets';
5
@import 'bootstrap';

Buat sebuah partial bernama _navigation.html.erb untuk menjaga kode navigasi; partial harus diletakkan di app/views/layouts. Buat partial tampak seperti di bawah.

1
#app/views/layouts/_navigation.html.erb
2
3
<nav class="navbar navbar-inverse">
4
  <div class="container">
5
    <div class="navbar-header">
6
      <%= link_to 'Pundit Blog', root_path, class: 'navbar-brand' %>
7
    </div>
8
    <div id="navbar">
9
 
10
    <ul class="nav navbar-nav pull-right">
11
      <li><% link_to 'Home', root_path %></li>
12
      <ul class="nav navbar-nav pull-right">
13
        <% if user_signed_in? %>
14
        <li><%= current_user.email %></li>
15
        <li><%= link_to 'Log out', destroy_user_session_path, method: :delete %></li>
16
        <% else %>
17
          <li><%= link_to 'Log In', new_user_session_path %></li>
18
          <li><%= link_to 'Sign Up', new_user_registration_path %></li>
19
        <% end %>
20
      </ul>
21
    </ul>
22
  </div>
23
</nav>

Untuk navigasi yang digunakan, kamu perlu merendernya di dalam layout aplikasi. Tweak layout aplikasimu menjadi seperti di bawah.

1
#app/views/layouts/application.html.erb
2
3
<!DOCTYPE html>
4
<html>
5
  <head>
6
    <title>Pundit-Blog</title>
7
    <%= csrf_meta_tags %>
8
9
    <%= stylesheet_link_tag    'application', media: 'all', 'data-turbolinks-track': 'reload' %>
10
    <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
11
  </head>
12
13
  <body>
14
    <%= render "layouts/navigation" %>
15
    <div id="flash">
16
      <% flash.each do |key, value| %>
17
        <div class="flash <%= key %>"><%= value %></div>
18
      <% end %>
19
    </div>
20
    <div class="container-fluid">
21
      <%= yield %>
22
    </div>
23
  </body>
24
</html>

Membuat User Model

Jalankan perintah untuk memasang Devise.

1
rails generate devise:install

Sekarang buat User Model mu.

1
rails generate devise User

Migrasikan database-mu.

1
rake db:migrate

Membuat Article Resources

Jalankan perintah untuk membuat article resource mu.

1
rails generate scaffold Articles title:string body:text

Ini akan membuat ArticleController dan Article Model. Juga akan membuat views yang diperlukan.

Sekarang migrasikan database-mu menggunakan:

1
rake db:migrate

Buka app/views/articles/_form.html.erb dan buat menjadi tampak seperti di bawah ini.

1
#app/views/articles/_form.html.erb
2
3
<%= form_for(article) do |f| %>
4
  <% if article.errors.any? %>
5
    <div id="error_explanation">
6
      <h2><%= pluralize(article.errors.count, "error") %> prohibited this article from being saved:</h2>
7
8
      <ul>
9
      <% article.errors.full_messages.each do |message| %>
10
        <li><%= message %></li>
11
      <% end %>
12
      </ul>
13
    </div>
14
  <% end %>
15
16
  <div class="field">
17
    <%= f.label :title %>
18
    <%= f.text_field :title %>
19
  </div>
20
21
  <div class="field">
22
    <%= f.label :body %>
23
    <%= f.text_area :body %>
24
  </div>
25
26
  <div class="actions">
27
    <%= f.submit %>
28
  </div>
29
<% end %>

Untuk index file, dia harus tampak seperti ini.

1
#app/views/articles/index.html.erb
2
3
<table class="table table-bordered table-striped table-condensed table-hover">
4
  <thead>
5
  <tr>
6
    <th>Title</th>
7
    <th>Body</th>
8
    <th colspan="3"></th>
9
  </tr>
10
  </thead>
11
12
  <tbody>
13
    <% @articles.each do |article| %>
14
    <tr>
15
      <td><%= article.title %></td>
16
      <td><%= article.body %></td>
17
      <td><%= link_to 'Show', article %></td>
18
      <td><%= link_to 'Edit', edit_article_path(article) %></td>
19
      <td><%= link_to 'Destroy', article, method: :delete, data: { confirm: 'Are you sure?' } %></td>
20
    </tr>
21
    <% end %>
22
  </tbody>
23
</table>
24
25
<br>
26
27
<%= link_to 'New article', new_article_path %>

Kode di atas menyusun artikel-artikel di halaman indeks ke dalam format tabel untuk membuatnya lebih mudah ditampilkan.

Buka berkas routes mu dan tambahkan routes untuk resource articles.

1
#config/routes.rb
2
3
...
4
  resources :articles
5
  root to: "articles#index"

Mengintegrasikan Pundit

Tambahkan Pundit gem ke Gemfile mu.

1
#Gemfile
2
3
...
4
gem 'pundit'

Jalankan perintah untuk memasang.

1
bundle install

Integrasikan Pundit dengan aplikasimu dengan menambahkan baris berikut ke ApplicationController mu.

1
#app/controllers/application_controller.rb
2
3
...
4
  include Pundit
5
...

Jalankan generator Pundit

1
rails g pundit:install

Ini akan membuat folder app/policies yang berisi class dasar dengan policies. Setiap policy adalah class Ruby dasar.

Seperti inilah tampilan dari base class policy.

1
#app/policies/application_policy.rb
2
3
class ApplicationPolicy
4
  attr_reader :user, :record
5
6
  def initialize(user, record)
7
    @user = user
8
    @record = record
9
  end
10
11
  def index?
12
    false
13
  end
14
15
  def show?
16
    scope.where(:id => record.id).exists?
17
  end
18
19
  def create?
20
    false
21
  end
22
23
  def new?
24
    create?
25
  end
26
27
  def update?
28
    false
29
  end
30
31
  def edit?
32
    update?
33
  end
34
35
  def destroy?
36
    false
37
  end
38
39
  def scope
40
    Pundit.policy_scope!(user, record.class)
41
  end
42
43
  class Scope
44
    attr_reader :user, :scope
45
46
    def initialize(user, scope)
47
      @user = user
48
      @scope = scope
49
    end
50
51
    def resolve
52
      scope
53
    end
54
  end
55
end

Membuat Article Policy

Sekarang kamu harus menulis policy mu sendiri. Untuk tutorial ini, kamu ingin untuk hanya mengizinkan pengguna terdaftar untuk membuat artikel baru. Di samping itu, hanya pembuat dari sebuah artikel yang bisa mengubah dan menghapus artikel.

Untuk mencapainya, article policy mu akan tampak seperti ini.

1
#app/policies/article_policy.rb
2
3
class ArticlePolicy < ApplicationPolicy
4
  def index?
5
    true
6
  end
7
8
  def create?
9
    user.present?
10
  end
11
12
  def update?
13
    return true if user.present? && user == article.user
14
  end
15
16
  def destroy?
17
    return true if user.present? && user == article.user
18
  end
19
20
  private
21
22
    def article
23
      record
24
    end
25
end

Di atas kita, kita mengizinkan siapaun ( pengguna terdaftar dan tidak terdaftar ) untuk melihat laman indeks. Unbtuk membuat artikel baru, sebuah pengguna harus terdaftar. Kamu menggunakan user.present? untuk mencari tahu apabila pengguna mencoba untuk melakukan aksi yang terdaftar.

Untuk memperbaharui dan menghapus, kamu harus pastikan hanya pembuat artikel yang bisa melakukan aksi tersebut.

Pada tahap ini, kamu perlu membuat sebuah hubungan antara Article dengan model User.

Kamu melakukannya dengan membuat sebuah migrasi baru.

1
rails generate migration add_user_id_to_articles user:references

Lalu, migrasikan database-mu dengan menjalankan kode:

1
rake db:migrate

Buka model User dan tambahkan baris yang membungkus hubungan.

1
#app/models/user.rb
2
3
...
4
  has_many :articles

Model Article-mu harus memiliki ini.

1
#app/models/article.rb
2
3
...
4
  belongs_to :user

Sekarang kamu harus memperbaharui ArticlesController sehingga ini tersinkron dengan apa yang telah kamu lakukan sejauh ini.

1
#app/controllers/articles_controller.rb
2
3
class ArticlesController < ApplicationController
4
  before_action :set_article, only: [:show, :edit, :update, :destroy]
5
6
  # GET /articles
7
  # GET /articles.json
8
  def index
9
    @articles = Article.all
10
    authorize @articles
11
  end
12
13
  # GET /articles/1
14
  # GET /articles/1.json
15
  def show
16
  end
17
18
  # GET /articles/new
19
  def new
20
    @article = Article.new
21
    authorize @article
22
  end
23
24
  # GET /articles/1/edit
25
  def edit
26
  end
27
28
  # POST /articles
29
  # POST /articles.json
30
  def create
31
    @article = Article.new(article_params)
32
    @article.user = current_user
33
    authorize @article
34
35
    respond_to do |format|
36
      if @article.save
37
        format.html { redirect_to @article, notice: 'Article was successfully created.' }
38
        format.json { render :show, status: :created, location: @article }
39
      else
40
        format.html { render :new }
41
        format.json { render json: @article.errors, status: :unprocessable_entity }
42
      end
43
    end
44
  end
45
46
  # PATCH/PUT /articles/1
47
  # PATCH/PUT /articles/1.json
48
  def update
49
    respond_to do |format|
50
      if @article.update(article_params)
51
        format.html { redirect_to @article, notice: 'Article was successfully updated.' }
52
        format.json { render :show, status: :ok, location: @article }
53
      else
54
        format.html { render :edit }
55
        format.json { render json: @article.errors, status: :unprocessable_entity }
56
      end
57
    end
58
  end
59
60
  # DELETE /articles/1
61
  # DELETE /articles/1.json
62
  def destroy
63
    @article.destroy
64
    respond_to do |format|
65
      format.html { redirect_to articles_url, notice: 'Article was successfully destroyed.' }
66
      format.json { head :no_content }
67
    end
68
  end
69
70
  private
71
    # Use callbacks to share common setup or constraints between actions.
72
    def set_article
73
      @article = Article.find(params[:id])
74
      authorize @article
75
    end
76
77
    # Never trust parameters from the scary internet, only allow the white list through.
78
    def article_params
79
      params.require(:article).permit(:title, :body, :user_id)
80
    end
81
end

Pada titik ini di aplikasimu, kamu telah sukses mengimplementasikan policy yang akan menghalangi beberapa bagian dari aplikasimu untuk beberapa pengguna.

Kamu bisa menambahkan pesan error standar yang akan tampil kapanpun seorang pengguna tak terdaftar mencoba untuk memasuki halaman terbatas. Untuk melakukannya tambahkan hal berikut ke ApplicationController mu.

1
#app/controllers/application_controller.rb
2
3
...
4
  rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized
5
6
  private
7
8
    def user_not_authorized
9
      flash[:warning] = "You are not authorized to perform this action."
10
      redirect_to(request.referrer || root_path)
11
    end

Kode ini secara sederhana me-render teks biasa yang akan memberi tahu pengguna bahwa dia tidak terotorisasi untuk melakukan aksi tersebut.

Jalankan:

1
$ rails server

Untuk menjalankan server Rails, buka browsermu ke https://localhost:3000 untuk melihat yang kamu miliki.

Kesimpulan

Di tutorial ini, kamu telah belajar cara bekerja baik dengan Devise dan Pundit. Kamu bisa membuat policy yang hanya mengizinkan pengguna terotorisasi untuk melihat bagian tertentu dari aplikasi.  Kamu juga telah membuat sebuah pesan error dasar yang tampil ketika pengguna tidak terotorisasi mencoba mengakses bagian terlarang dari aplikasi.

Kamu bisa belajar lebih banyak mengenai Pundit dengan mengecek laman GitHubnya

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.