четвер, 26 травня 2011 р.

Огляд Padrino і OmniAuth

У цій статті буде показано, як з’єднати модуль автентифікації Access Control, описаний тут, з OmniAuth middleware для Rack.

Система аутентифікації і контролю доступу Padrino забезпечує простий спосіб створювати свої системи аутентифікації. У поєднанні з OmniAuth ви можете легко використовувати її для аутентифікації за допомогою різних методів. Читайте нижче для більш докладної інформації про те, як інтегрувати їх.

У цій статті ми розглянемо дві теми:
  • Інтеграцію Padrino Admin Authentication в усі додатки вашого проекту
  • Створення власних стратегій аутентифікації

На відміну від оригінальної статті, де у якості ORM використовується ActiveRecord, тут ми будемо використовувати DataMapper, про який я писав тут. Можливо вам необхідно познайомитись з фреймворком Padrino, про який я писав тут і тут. І про OmniAuth тут.

Перш ніж ми почнемо, важливо відзначити, що наша вбудована аутентифікація, що базується на ролях, може взаємодіяти з іншими системами. Потрібно лише внести зміни у файли session.rb і account.rb для того щоб додати свій власний код.

Отже, давайте почнемо зі створення проекту використовуючи DataMapper:

$ padrino g project omniauth_dm --orm datamapper --tiny
$ cd omniauth_dm/
$ bundle install

Додаємо модель Account:
$ padrino g model Account name:string email:string role:string uid:string provider:string
name ім'я користувача
email email користувача
role роль контролю доступу
uid ідентифікатор користувача omniauth
provider постачальник omniauth

Створюємо базу даних:
$ padrino rake dm:migrate

Відкриваємо файл Gemfile і додаємо рядок:
# Gemfile
gem 'omniauth'

Запускаємо bundle install щоб встановити залежності:
$ bundle install

Тепер потрібно додати OmniAuth middleware. Тут доступні два шляхи:
  • Додати middleware для всьо проекту(таким чином всі піддодатки зможуть використовувати його)
  • Додати middleware тільки для додатку, який його потребує

В нашому прикладі, нам потрібно просто змінити app/app.rb:
# app/app.rb
class OmniauthDm < Padrino::Application
# ...
  use OmniAuth::Builder do
    provider :vkontakte, 'application_id', 'secure_key'
    provider :facebook, 'app_id', 'app_secret'
  end
# ...
end

Якщо ви вибрали multiapp сценарій вам необхідно відредагувати config/boot.rb:
# config/boot.rb
Padrino.use OmniAuth::Builder do
  provider :vkontakte, 'application_id', 'secure_key'
  provider :facebook, 'app_id', 'app_secret'
end

# before the line
Padrino.load!

Для отримання app_id і app_secret для Facebook необхідно:
  • Перейти до сторінки розробників Facebook
  • Натиснути Створити новий додаток
  • Вказати ім’я додатку і заповнити форму
  • Закінчивши, ви зможете змінити налаштування. Перейдіть у розділ Website
  • Додати до site url: http://localhost:3000
  • Додати до domain: localhost
  • Записати "Application ID" і "Application Secret" до OmniAuth::Builder

Для отримання application_id і secure_key для ВКонтакте необхідно:
  • Перейти до сторінки для розробників ВКонтакте
  • Натиснути Створити додаток
  • Вказати ім’я додатку і заповнити форму
  • Закінчивши, ви зможете змінити налаштування.
  • Додати до "Адреса сайту": http://localhost:3000/
  • Додати до "Базовий домен": localhost
  • Записати "ID додатку" і "Захисний ключ" до OmniAuth::Builder

ПРИМІТКА: Для домену не важливо чи шлях існуючий, тому що OmniAuth змінює callback url.

Далі, ми можемо інтегрувати нашу системи аутентифікації у app/app.rb:
class OmniauthDm < Padrino::Application
  # ...
  register Padrino::Admin::AccessControl

  set :login_page, "/" # визначає URL для входу

  access_control.roles_for :any do |role|
    role.protect "/profile"
    role.protect "/admin"
  end

  # тепер додаємо роль для користувачів
  access_control.roles_for :users do |role|
    role.allow "/profile"
  end
  
  # ...
end

І додамо декілька маршрутів у app/controllers.rb:
OmniauthDm.controllers  do
# ...
  get :index do
    haml <<-HAML.gsub(/^ {6}/, '')
      - if !current_account.nil?
        Hello
        =link_to(current_account['name'], url(:profile))
        %br
        =button_to('Log Out', url(:destroy), :method => :delete)
      - else  
        Login with
        =link_to('Facebook', '/auth/facebook')
        or
        =link_to('Vk', '/auth/vkontakte')
    HAML
  end

  get :profile do
    content_type :text
    current_account.to_yaml
  end

  delete :destroy do
    set_current_account(nil)
    redirect url(:index)
  end

  get :auth, :map => '/auth/:provider/callback' do
    auth    = request.env["omniauth.auth"]
    account = Account.find_by_provider_and_uid(auth["provider"], auth["uid"]) || 
              Account.create_with_omniauth(auth)
    set_current_account(account)
    redirect "http://" + request.env["HTTP_HOST"] + url(:profile)
  end

# ...
end

Вище ми посилаємося до методу Account.create_with_omniauth. Крім того для забезпечення працездатності потрібно створити методи find_by_id та find_by_provider_and_uid, які в ActiveRecord визначаються автоматично завдяки так званим "Dynamic attribute-based finders". Відкрийте app/models/account.rb і додате:
class Account
# ...
  def self.find_by_id(id)
    get(id) rescue nil
  end

  def self.find_by_provider_and_uid(provider, uid)
    first(:provider => provider, :uid=> uid) rescue nil
  end

  def self.create_with_omniauth(auth)
    account = Account.new
    account.provider = auth["provider"]
    account.uid      = auth["uid"]
    account.name     = auth["user_info"]["name"] if auth["user_info"]
    account.email    = auth["user_info"]["email"] if auth["user_info"] # тільки для Facebook. ВКонтакте не повертає email користувача :(
    account.role     = "users"
    account.save

    return account
  end
# ...
end

Настрав час запустити сервер і подивитись, що у нас вийшло:
$ padrino start

Перейдіть до http://localhost:3000/profile
Це приведе вас до початкової сторінки login_page, в нашому випадку /.
Тепер ви можете увійти тут http://localhost:3000

Це все, що вам потрібно для налаштувати скелету системи аутентифікації в Padrino.

Немає коментарів: