how to Use zend_auth as a plugin - php

I'm working on my first user login in Zend Framework, but I'm a little confused with Zend_Auth. All the articles I read about it use it directly in the controller. But to me, it makes more sense, to work as a plugin
What do you guys think?

You can use it as a plugin, the only downside is that if you initialize the plugin in your bootstrap, then the plugin will be executed for every controller and action, since it would have to run before your controller.
You could extend Zend_Auth and add extra methods to set up the auth adapter and manage the storage, and then you can just call Your_Custom_Auth::getInstance() to get the auth instance and then you can check for auth in the preDispatcth() part of your controllers that need auth.
This way you can easily work with zend_auth in multiple places with less code
<?php
class My_User_Authenticator extends Zend_Auth
{
protected function __construct()
{}
protected function __clone()
{}
public static function getInstance()
{
if (null === self::$_instance) {
self::$_instance = new self();
}
return self::$_instance;
}
// example using zend_db_adapter_dbtable and mysql
public static function getAdapter($username, $password)
{
$db = Zend_Controller_Front::getInstance()
->getParam('bootstrap')
->getResource('db');
$authAdapter = new Zend_Auth_Adapter_DbTable($db,
'accounts',
'username',
'password');
$authAdapter->setIdentity($username)
->setCredential($password)
->setCredentialTreatment(
'SHA1(?)'
);
return $authAdapter;
}
public static function updateStorage($storageObject)
{
self::$_instance->getStorage()->write($storageObject);
}
}
// in your controllers that should be fully protected, or specific actions
// you could put this in your controller's preDispatch() method
if (My_User_Authenticator::getInstance()->hasIdentity() == false) {
// forward to login action
}
// to log someone in
$auth = My_User_Authenticator::getInstance();
$result = $auth->authenticate(
My_User_Authenticator::getAdapter(
$form->getValue('username'),
$form->getValue('password'))
);
if ($result->isValid()) {
$storage = new My_Session_Object();
$storage->username = $form->getValue('username');
// this object should hold the info about the logged in user, e.g. account details
My_User_Authenticator::getInstance()->updateStorage($storage); // session now has identity of $storage
// forward to page
} else {
// invalid user or pass
}
Hope that helps.

"Plugin" in ZF doesn't only mean "front controller plugin", also Action helpers, view helpers...
ZF guru Matthew Weier O'Phinney wrote an excellent article about creating action helpers, and guess what ?..
He illustrates it with an Auth widget !
http://weierophinney.net/matthew/archives/246-Using-Action-Helpers-To-Implement-Re-Usable-Widgets.html
don't forget to read the articles comments, as a lot of interesting Q&A are handled there

Related

PHP OOP-based login system

Lets say I am building an OOP-based user authentication system, and I would like to incorporate the following principles: Direct Injection, Inheritance, Encapsulation, Polymorphism and the Single Responsibility Principle.
My background in programming is has always relied on procedural programming, and thus, am finding it difficult to really put these practices into correct use.
Assume I have these classes:
class Config
{
public function set($key, $value);
public function get($key, $default = null);
}
class User
{
public function __construct(PDO $dbh, $id = null);
public function setProfile(Profile $profile);
}
class Auth
{
public function __construct(Config $config);
public function login($username, $password, $keepLoggedIn = true);
public function isLoggedIn();
public function getLoggedInUser();
public function logout();
public function register(array $data);
}
class Session
{
public function start($sessionName = null);
public function write($key, $value);
public function read($key, $default = null);
}
class Profile
{
public function setAddress(Address $address);
public function setName($name);
public function setDOB(DateTime $date);
public function getAge();
}
class Validator
{
public function validate($input);
}
I have intentionally left off the function bodies to keep things simple.
To the best of my knowledge, I believe I'm using the principles correctly. However, I am still unclear as to how you would connect classes like: the Validator to the User model, the User model to the Auth and the Session to the Auth class. All of which depend on each other.
You are on the right track. The way these classes connect to each other is called extending. I tend to go towards an MVC setup, meaning Model, View, Controller.
Your logic goes into the controller, all your DB queries and concrete back end methods go in the model. The controller receives requests and returns responses. It's the middleman. It talks to the back end after a request has been made to it, and feeds the front in via response.
So you have a core controller (keep it bare minimal), then each class you make extends the core controller. So your controller is where you tie all this together.
<?php
//your main core controller, where you load all these things you need avilable, so long as this class is extended
class CoreController {
public $auth
public $session;
public $view;
function construct__ ()
{
$this->auth = instantiateAuthClassHere();
$this->session = instantiateSessionClassHere();
$this->view = instantiateViewClassHere();
}
public function anotherHelperForSomething(){
//helper stuff for this method
}
}
//index, page, or content controller, depending on how many you need, i.e. if you want a controller for each page, thats fine, e.g indexController, etc..
//this is the middle man, has logic, receives requst, returns response to view.
class Controller extends CoreController {
public function index (){
$userModel = new userModel();
//do something with this
$session = $this->session;
$content = 'some html';
$userInfo = $userModel->getUsers();
$view = $this->view->render( array(
'content' => $content,
'userInfo' => $userInfo,
));
return $view;
}
}
//Core LIbraries
class Validator {
//your validator stuff
}
//Core LIbraries
class Session {
//your validator stuff
}
//Core LIbraries
class Auth {
//your validator stuff
}
class CoreModel{
public $validator;
function __construct(){
$this->validator = instantiateValidatorClassHere();
}
}
//a user model class (back end). you want a model class for each db table pretty much.
class UserModel extends CoreModel {
// if you need the validator anywhere inside this class, its globally available here inside any class that extends the CoreModel, e.g. $this->validator->methodName()
public function getUsers (){
$sql = 'SELECT * from users';
$result = $db->get($sql);
return $result;
}
}
Notice, on the Controller, this is a generic name for something like indexController, or anything custom. Also, I have the word extends there. It inherits all the objects from the parent that it extends. Inside it, now they will be available via $this->. See my example where I get $this->session.
Try to avoid constructs - you probably don't need them anywhere except for the core, and under special circumstances, which you might then need to check for yourself before you do even that. I dont use constructs much anymore. It can be a bit clunky and unmanageable.

How to access the user object in the form configure method

I'm currently doing this kind fo thing with symfony forms
$this->myForm = new MyForm();
$this->myForm->customConfigureMethod($this->getUser()->getGuardUser());
because I need to configure a DoctriineChoice widget on the basis of the user.
I would rather do this kind of thing
$this->myForm =new myCustomConfiguredForm($this->getUser()->getGuardUser());
With the customisation being part of the form instantiation.
Anyone know how I could achieve this? I think I might be a bit unclear about the difference between the configure() and setup() functions for the forms so can't think clearly about it.
You shpuld pass the user object as an option. Here is an exapmle:
class ProductForm extends BaseProductForm
{
public function configure()
{
// or use an instance variable if you need the user in an another method too
$user = $this->getOption('user');
if (!$user instanceof sfBasicSecurityUser)
{
throw new InvalidArgumentException('A user object is required as "user" option in ' . __METHOD__);
}
// do something with the user...
}
}
$form = new ProductForm(array(), array('user' => $this->getUser()));

Zend Framework repeated functionality

I've recently started working with Zend Framework and I've absolutely fallen in love with it and I've even decommissioned my own framework in favour of it.
But I'm missing something that is probably so painfully obvious you'll chuckle a little bit.
I have a login system and in every controller I have to put the check for the login status, I had a look at accessing the Zend Session Storage in the Bootstrap but I'm on bit of a deadline and can't afford to waste time, is there a better way to check IE in the bootstrap? instead of repeating 20+ lines of code and functionality in every controller.
Thanks in advance!
You can use a controller plugin for this. See: http://framework.zend.com/manual/en/zend.controller.plugins.html
class Your_Plugin_Login extends Zend_Controller_Plugin_Abstract
{
public function preDispatch(Zend_Controller_Request_Abstract $request)
{
if (!Zend_Auth::getInstance()->hasIdentity()) {
// send the user to the login page
$request->setControllerName('login')
->setActionName('login');
}
}
}
replace the controller/action names with whatever is appropriate for your login page, and 'Your' with your application's namespace.
You then register the plugin with the Front controller either in application.ini or in your bootstrap with Zend_Controller_Front::getInstance()->registerPlugin(new Your_Plugin_Login());.
Edit: If you want to put the user details in the view as well, you can do:
$layout = Zend_Layout::getMvcInstance();
$view = $layout->getView();
$view->user = Zend_Auth::getIdentity();
You can write your own controller that inherits from Zend Controller (call it e.g. DaveMac_Controller - you can define the prefix in the app's config file so that the app can autoload it (and you need to be careful with which directory you save your class in)). In this class's construct function run the check for authentication. Then change all your page's controllers to inherit from DaveMac_Controller rather than the default zend one.
If I was at home I could copy and paste some code as an example, but at work right now so hopefully the above outline is enough.
*edit Good ol' dropbox :)
application.ini
includePaths.library = APPLICATION_PATH "/../library"
autoloaderNamespaces[] = "DaveMac_"
/../library/DaveMac/Controller/Action.php
<?php
class DaveMac_Controller_Action extends Zend_Controller_Action {
protected $acl;
protected $user;
protected $userRole;
public function init() {
//retrieve and store user details
$auth = Zend_Auth::getInstance();
if($auth->hasIdentity()){
$user = $auth->getIdentity();
$this->user = $user;
$this->view->user = $user;
$this->userRole = $user->role;
} else {
$this->userRole = "guest";
}
//Initialise access control list
$this->acl = new DaveMac_Acl();
}
protected function checkAuth($pageLevel, $redirect = "/") {
if($this->user) {
if(!$this->acl->isAllowed($this->userRole, $pageLevel)) {
$this->_redirect($redirect);
}
} else if ($pageLevel != DaveMac_Resources::PUBLIC_ONLY_PAGE) {
$this->_redirect('/login/returnurl/' . str_replace('/','-',$this->getRequest()->getRequestUri()));
}
}
}
You will probably already have your own functioms fdor checking authentication, but I thought I'd leave mine in anyway

Critique abstract class for handling GET and POST requests?

I'm only interested in handling GET or POST requests, so I designed this abstract class to determine which request has been made and to subsequently call the appropriate function. I would really appreciate feedback on this. Thanks!
PS I think this should be a community wiki, but I'm not sure how to set it as that.
abstract class AHttpRequestHandler
{
public function handleRequest()
{
if($_SERVER['REQUEST_METHOD'] == 'POST') {
$this->handlePostRequest();
} else if($_SERVER['REQUEST_METHOD'] == 'GET') {
$this->handleGetRequest();
} else {
$this->handleIllegalRequest();
}
}
abstract protected function handleGetRequest();
abstract protected function handlePostRequest();
protected function handleIllegalRequest()
{
throw new Exception('Illegal request detected in HttpRequestHandler::handleIllegalRequest().');
}
}
In response to comments:
I will only be handling one or the other (GET or POST), never both at the same time.
Either an HTML form will be submitted via POST, or a redirect will be made with a query string, which will be a GET request. I am not familiar with how a mixed request could be made (both GET and POST), but since this is a personal project I have control over whether it happens or not.
I use the AHttpRequestHandler class (above) by implementing the handleGetRequest() and handlePostRequest() methods in a sub-class, which is and abstract controller, AController. Then, for each page of my CMS, I create a sub-class of AController, such as ImageUpload or ImageDetailsEditor. I can provide more details if it will help.
Here are the AController, Controller, and View classes:
AController
abstract class AController extends AHttpRequestHandler
{
protected $view;
public function __construct()
{
$this->handleRequest();
}
protected function handleGetRequest()
{
throw new Exception('handleGetRequest not yet implemented.');
}
protected function handlePostRequest()
{
throw new Exception('handlePostRequest not yet implemented.');
}
abstract protected function initView();
}
Controller
class Controller extends AController
{
protected $content;
public function __construct()
{
$this->view = new View();
parent::__construct();
}
protected function handleGetRequest()
{
$this->content = 'GET Request';
$this->initView();
}
protected function handlePostRequest()
{
$this->content = 'POST Request';
$this->initView();
}
protected function initView()
{
$this->view->content = $this->content;
$this->view->display();
}
}
View
//An over-simplified view for example use only
class View
{
public $content;
public function display()
{
echo "<p>$this->content</p>";
}
}
The actual use:
require_once 'Controller.php';
$controller = new Controller();
First of all you can make a GET request and a POST request in the same time. Think of a form that you post but the url has some variables in the query ( get ).
1.I don't understand the need for such a class but the first thing you could do is make two separate classes for post and get that extend the AHttpRequestHandler class. That way you only need an abstract function handleRequest that you will implement in the child classes.
2.You should apply "Intention Revealing Names". Your class should be RequestHandler and your methods should not contain Request in them. You know that from the class name.
3.Think about this: you might need to handle the post request in one controller. So you will have to add the second abstract method each time just to respect the abstract class.
4.You should not make circular calls between classes ( The Hollywood principle ). handleRequest is called from the child class, and then the parent calls handleGetRequest or handlePostRequest from the child.
Like I said, you are the developer, you know each controller what will use:POST or GET ( what about COOKIEs? ), so you can handle them at controller level without the need to extra classes just for the sake of it.
see ref
see ref
see ref
see ref
And the Controller should receive a request (command), not extend the request to keep things apart. Have no catch phrase for that, perhaps seperation of concerns. That's an extension to 1. above but only if you really need a request object.
Having an abstract class for requests is a good idea and it is there in all frameworks. But I dont think its good to extend this class by all controllers. A better solution will be to separate this to two, an abstract request class and base controller class. In request class you can have methods to identify whether it is a get request or post request, like
class Request{
public function isPost() {
return ($_SERVER['REQUEST_METHOD'] == 'POST');
}
public function isGet() {
return ($_SERVER['REQUEST_METHOD'] == 'GET');
}
}
Also we will have a base controller class with at least the following options
class Controller
{
public $request;
public function __construct() {
$this->setRequest(new Request());
}
public function setRequest(Request $request) {
$this->request = $request;
}
}
All our client controllers will extend the base controller as usual. The advantage of this method is client controllers will have the freedom to determine the request type. if they want to make use of GET and POST request at a time, that also will be possible. The above given is of course an incomplete one. You need to add more methods to the base classes or not is your choice.

Best way to deal with session handling in Zend Framework

So I'm starting up in Zend framework and looking to implement a site-wide "User" session.... something I can easily access from ALL modules/controllers in the application.
I'm like, should I make a new namespace in the library and extend the controller, like:
class MYCUSTOMLIB_Controller_Action extends Zend_Controller_Action
{
protected $_userSession;
function preDispatch(Zend_Controller_Request_Abstract $req)
{
$this->_userSession = new Zend_Session_Namespace('user');
}
}
ANd then have all my controllers/modules/etc extend from that?
Or should I create a Plugin or what? How would you go about making this plugin to pass the user session to the controller?
Or do I do it in the bootstrap?? Again how to pass to controller?
Also should I use Zend_Session_Namespace or Zend_Http_Cookie and also how do I encrypt and xss clean the cookie or is that done automagically?
I would initialise in the bootstrap too:
//Bootstrap.php
protected function _initUserSession()
{
return new Zend_Session_Namespace('user');
}
Then I would use an action helper:
// library/App/Controller/Action/Helper/Session.php
class App_Controller_Action_Helper_Session extends Zend_Controller_Action_Helper_Abstract
{
function direct()
{
return $this->getFrontController()->getParam('userSession');
}
}
You access it in your controller like this:
function indexAction()
{
$session = $this->_helper->session;
}
You should initialize your session in the bootstrap. You can either put it in the Zend_Registry and access it that way or from your controllers you can access your bootstrap by calling $this->getInvokeArg('bootstrap').
// in your controllers
public function init()
{
$bootstrap = $this->getInvokeArg('bootstrap');
$this->_session = $bootstrap->getResource('session');
}

Categories