Creare un admin area in rails3 usando l’autenticazione scritta in devise
1)aggiungere il namespace "admin" in routes.rb, ed abbinarlo ai controller per i quali vogliamo l'autenticazione, in questo esempio "questions".
namespace :admin do match '/' => 'questions#index' resources :questions end
2In views/layout creare il layout admin.html.erb specificando le parti che vogliamo mostrare nella sezione admin.
3) creare il controller admin_controller.rb in controllers/admin e utilizzare "autenticate_user" (vedere il tutorial su devise) per verificare che un utente sia loggato
class Admin::AdminController < ApplicationController layout "admin" before_filter :authenticate_user! end
4) Sempre in controllers/admin, creare il file questions_controller.rb
class Admin::QuestionsController < Admin::AdminController
def index
@questions = Question.all
respond_to do |format|
format.html # index.html.erb
end
end
end
5)in views/admin creaimo la cartella "questions" e inderiamo tutte le views richieste dal controller controllers/admin/questions_controller.rb, in questo caso, solo il file index.html.rb
6
Le action dei form all'interno delle views in views/admin/questions devono rimandare ai controllers in controllers/admin/question controller. Quindi, sempre tenendo "question" come esempio da:
<%= form_for(@answer) do |f| %>
dobbiamo passare a
<%= form_for(:admin, @answer) do |f| %>
CakePHP 2.0 Authentication
Con CakePHP 2.0 ci sono tante novità. In questo post spiegherò come realizzare una semplice sistema di autenticazione per l'admin area, in cui abbiamo differenti users che possono accedervi, ma solo uno e' l'admin, gli altri sono amministratori con funzioni limitate. Per un piccolo sistema, con pochi utenti, ho deciso di non usare ACL, troppo ingombrante e dispendiosa, ma di scrivere un metodo apposito, allowedGroups da richiamare nelle azioni in cui voglio verificare che solo un determinato gruppo di utenti possa accedere a determinate funzioni. Alcune note sulle novità che influiscono sull'autenticazione.
- Sono supporati diversi tipi di autenticazione, configurabili tramite degli handlers, quello usato in questo articolo è il FormAuthenticate handler di default.
- Nelle versioni precedenti di CakePHP 2.0 bastava aggiungere l'Auth Component e chiamare la funzione login nello user controller, nella nuova versione, nella nostra function login, dovremo chiamare manualmente "$this->Auth->login()"
- La password non viene più crittata di default, il metodo va chiamato manualmente
Creiamo un database per gli users e uno per i groups
CREATE TABLE IF NOT EXISTS `groups` ( `id` int(11) NOT NULL AUTO_INCREMENT, `nome` varchar(70) NOT NULL, `description` text NOT NULL, PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=2 ; CREATE TABLE IF NOT EXISTS `users` ( `id` int(11) NOT NULL AUTO_INCREMENT, `group_id` int(11) NOT NULL, `name` varchar(70) NOT NULL, `email` varchar(70) NOT NULL, `password` varchar(60) NOT NULL, `description` text NOT NULL, `created` datetime NOT NULL, `modified` datetime NOT NULL, PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=2 ;
Creiamo i relativi controllers, models e views usando "cake bake".
Andiamo ad eliminare tutte le action e tutte le view che non saranno accessibili al di fuori dell'admin area. Rimuoviamo quindi,per esempio, dallo UserController le action delete, add, edit. Aggiungiamo i metodi per il login ed il logout dello user in app/Controllers/UsersController.php
/**
* Users Controller
*
* @property User $User
*/
class UsersController extends AppController {
public function beforeFilter() {
parent::beforeFilter();
$this->Auth->allow('admin_login');
}
/**
* login method
*
* @return void
*/
public function admin_login() {
if ($this->request->is('post')) {
if ($this->Auth->login()) {
$user_id = $this->Auth->user('id');
$group_id = $this->Auth->user('group_id');
$group = $this->User->getGroup($group_id);
$this->Session->write('Auth.User.group', $group);
$this->redirect(array('action' => 'profile', $user_id,
'admin' => true));
} else {
$this->Session->setFlash(__('email o password errata'),
'default', array(), 'auth');
}
}
}
/**
* logout method
*
* @return void
*/
public function admin_logout() {
$this->Session->setFlash('Sei ora sloggato dal sistema');
$this->Session->destroy();
$this->redirect($this->Auth->logout());
}
Come vedete ho utilizzato il metodo getGroup, che dovro' scrivere nel mio App/Model/User.php
function getGroup($group_id){
$conditions = array('id' => $group_id);
$group = $this->Group->field('nome', $conditions);
if ($group) {
return $group;
}
else {
return false;
}
}
Passiamo ora all'AppController.php, richiediamo l'autenticazione per chiunque voglia accedere all'admin area, e creiamo in App/Views/Layouts il file admin.ctp che conterrà il layout specifico per l'admin. Inoltre, andiamo a scrivere nella sessione il nome del gruppo, in modo da poterlo sempre leggere nella variabile $current_user, e da poterlo utilizzare sia per decidere quali elementi nelle view mostrare, sia per verificare che il nome del gruppo sia nell'elenco delle funzioni che vogliamo restringere solo ad alcuni gruppi.
class AppController extends Controller {
var $components = array('Auth','Session');
var $helpers = array('Session','Form','Html');
var $current_user = false;
function beforeFilter() {
if (isset($this->request->params['admin'])) {
$this->layout = 'admin';
$this->Auth->logoutRedirect = '/';
$this->Auth->authenticate = array(
AuthComponent::ALL => array(
'fields' => array(
'username' => 'email',
'password' => 'password'),
'userModel' => 'Users.User'
), 'Form'
);
} else {
$this->Auth->allow();
}
$this->current_user = $this->Auth->user();
}
function beforeRender() {
$this->set('current_user',$this->current_user );
}
function allowGroups($groups = array(), $options = array()){
if (empty($options['message'])) {
$options['message'] = 'non sei autorizzato ad accedere';
}
$group = $this->Session->read('Auth.User.group');
if (in_array($group, $groups) || ($groups[0] == "*")) {
return true;
}
else {
$this->Session->setFlash(__($options['message']));
$this->Session->destroy();
$this->redirect($this->Auth->logout());
}
}
}
Creaimo il file Views/Users/admin_login.ctp
<?
echo $this->Session->flash('auth');
echo $this->Form->create('User', array('url' => array('controller' => 'users', 'action' =>'login')));
echo $this->Form->input('User.email', array('label'=>'Email'));
echo $this->Form->input('User.password', array('label'=>'password'));
echo $this->Form->end('Loggati');
?>
Qui sotto un esempio su come utilizzare allowGroups.
#se voglio autorizzare tutti i gruppi esistenti
$this->allowGroups(array('*'));
#se voglio autorizzare solo il gruppo admin
$this->allowGroups(array('admin'));
#se voglio autorizzare admin e supervisori
$this->allowGroups(array('admin','supervisori'));
Ruby on Rails Devise authentication
Aggiungete devise nel vostro Gemfile e lanciate "bundle install".
Ora create i file di configurazione necessari:
rails generate devise:install
Definite l'url che verra' utilizzata per i link di conferma sulle email di registrazione. In configurations/environments/development.rb
#Devise email
config.action_mailer.default_url_options = { :host => 'localhost:3000' }
Creiamo il file di migrazione e lo user model
rails generate devise User
Nello User model appena creato Models/user.rb , possiamo scegliere quali moduli includere od escludere, semplicemente togliendo quelli di default o aggiungendo quelli presenti nel commento del file user.rb. A mio avviso quelli di default vanno piu' che bene.
class User < ActiveRecord::Base # Include default devise modules. Others available are: # :token_authenticatable, :confirmable, :lockable and :timeoutable :recoverable, devise :database_authenticatable, :registerable, :rememberable, :trackable, :validatable # Setup accessible (or protected) attributes for your model attr_accessible :email, :password, :password_confirmation, :remember_me end
Ora aprite il file Migrations/***devise_create_users.rb e commentate o aggiungete i moduli che avete aggiunto to tolto nello User Model. Poi fate la migrazione.
rake db:migrate
Con "rake routes" potete vedere tutte le routes messe a disposizione dalla vostra applicazione. Trovo questo comendo davvero molto utile, anche nel debug di un applicazione, o quando vogliamo verificare che ogni action abbia la sua view ben fatta.Un'altra cosa che cakePHP non ha (cominciano ad essere tante).
Ora se puntate il browser alla seguente url /users/sign_in potrete loggarvi. Per registrarvi andate qui /users/sign_up. Io ho incontrato il seguente errore:
No route matches "/users/sign_up"
Per risolverlo ho definito lo scope nel file routes, come spiegato qui http://stackoverflow.com/questions/2349604/using-devise-with-rails-3-beta
Per visualizzare i messaggi d'errore nella vostra applicazione, aggiungete queste linee di codice nel vostro application.html.erb.
<%- flash.each do |name, msg| -%>
<%= content_tag :div, msg, :id => "flash_#{name}" if msg.is_a?(String) %>
<%- end -%>
Sempre in application.html.erb, aggiungete i link alle azioni sign_in e sign_out. Scoprite i relativi path attraverso "rake routes" e aggiungete questo codice dopo il tag body:
<div id="user_nav">signed in as</div>
Per circoscrivere il raggio d'azione di un utente non loggato all'interno della nostra applicazione, definiamo quali azini sono ad esso concesse. Poniamo il caso che solo un utente loggato puo' creare o modificare un post, mentre l'utente sloggato puo' solo vederli.
Nel nostro "posts_controller" aggiungiamo la seguente funzione:
before_filter :authenticate_user!, :except => [:index, :show]
Per modificare le views di devise, lanciamo "rails g devise:views", ed otterremo un output simile a questo:
create app/views/devise create app/views/devise/confirmations/new.html.erb create app/views/devise/mailer/confirmation_instructions.html.erb create app/views/devise/mailer/reset_password_instructions.html.erb create app/views/devise/mailer/unlock_instructions.html.erb create app/views/devise/passwords/edit.html.erb create app/views/devise/passwords/new.html.erb create app/views/devise/registrations/edit.html.erb create app/views/devise/registrations/new.html.erb create app/views/devise/sessions/new.html.erb create app/views/devise/shared/_links.erb create app/views/devise/unlocks/new.html.erb
Modifichiamo i seguenti file a nostro piacimento. Se vogliamo modificare i messaggi d'errore, mettiamo mano al file config/locales/devise_en.yml. Se invece vogliamo modificare le regole di controllo per la registrazione (es passare da "Password is too short (minimum is 6 characters)" a "Password is too short (minimum is 8 characters)") modifichiamo il file initializers/devise.rb.
Per modificare le URL di default con le quali avvengono le azioni di login e logout, apriamo il file routes.rb
devise_for :users, :path_names => {:sign_in => "login", :sign_up => "logout"}
.
Il nostro futuro user si logga inserendo email e password, come fare se in futuro ci venisse richiesto di sostituire il campo email con il campo username? prima di tutto aggiungiamo la colonna necessaria:
rails generate migration AddUsernameToUsers username:string rake db:migrate
Ora in config/initializers/devise.rb aggiungiamo
config.authentication_keys = [ :username ]
Questo tutorial e' tratto dai seguenti railcasts:
http://railscasts.com/episodes/209-introducing-devise
http://railscasts.com/episodes/210-customizing-devise
Qua la pagina del progetto su github.