Yii framework custom 403 handling for specific controller - yii

I have a requirement that checks if the current loged in user is a payed member or not for a set of action. if the user in not a payed member then he has to be sent to the take membership page. following is the code. In the controller (Yii framework )
public function accessRules()
{ return array(
array('allow',
'actions'=>array('enroll','index','admin','suggesttags'),
'users'=>array('#'),
),
array('allow',
'actions'=>array('view', 'read'),
'users'=>array(Yii::app()->user->name),
'expression' => 'Yii::app()->controller->hasPaied()'
),
now the hasPayed() functions returns false for unpaid member and at present user is redirected to 403 exception.
I want to customize the 403 exception page to 'take membership' page. Is there a way to do it? so that all the exception raised from this specific controller\action are send to the take membership page and rest 403 exception remain unchanged ?

Try using deniedCallback from CAccessControlerFilter.
// optional, the denied method callback name, that will be called once the
// access is denied, instead of showing the customized error message. It can also be
// a valid PHP callback, including class method name (array(ClassName/Object, MethodName)),
// or anonymous function (PHP 5.3.0+). The function/method signature should be as follows:
// function foo($user, $rule) { ... }
// where $user is the current application user object and $rule is this access rule.
// This option is available since version 1.1.11.
'deniedCallback'=>'redirectToDeniedMethod',

Related

CakePhp4 Implementing deprecated Controller::isAuthorized() with the new Authentication & Authorization plugins

I'm looking how to to implement the deprecated Controller::isAuthorized() with the new Authentication & Authorization plugins but I could not find the way.
For example in the method RequestPolicyInterface::canAccess() called by the RequestAuthorizationMiddleware I could not get an instance of the current Controller.
Any idea ??
Thanks.
Policies apply to, and receive resources, the request policy applies to requests, and will receive the current request object accordingly, additionally to the current identity which policies do always receive.
The specific signature for the canAccess method in this case is:
canAccess(\Authorization\IdentityInterface $identity, \Cake\Http\ServerRequest $request)
eg, the method will receive the current request in the second argument, and you can obtain the routing information from the request parameters::
public function canAccess($identity, ServerRequest $request)
{
if (
!$request->getParam('plugin') &&
!$request->getParam('prefix') &&
$request->getParam('controller') === 'Articles'
) {
return $identity->role->name === 'admin';
}
return true;
}
This would allow only users with the role admin to access to the (non-plugin/prefixed) Articles controller, and allow access to all other controller to anyone.
Note that you will not receive an instance of the controller, as that specific check is a) not made against a controller object but a request object, and b) happens before the controller is even instantiated.
See also
Authorization Cookbook > Request Authorization Middleware

Customise Behaviour On Authorize Failed

I have successfully implemented group based authorization in an MVC application by using the [Authorize(Roles = "Admin")] tags in my controller.
However, the default behaviour when a user requests a page they are not authorized to view is to redirect them to the login page. This far from intuitive, and causes too much confusion amongst users who will repeatedly try to login.
Instead I would like to display a custom screen, or at the very least have a message display on the login screen stating that they are already logged in, but with insufficient privileges.
A User.Identity.IsAuthenticated tag exists, which can be used in the base logic, but there doesn't appear to be a similar IsAuthorised tag.
How can this behaviour be implemented?
I believe you have already partly solved your problem. This because because when authorization fails, the user will be redirected to login page. Verify if the user is authenticated before displaying the login view. If they are authenticated re-direct them to appropriate page instead.The code snippet below will not display login view if the user is authenticated and the cookie has not expired. They will be redirected to "DashboardOrSomeOtherView.cshtml" instead
[HttpGet]
public ActionResult Login(string returnUrl)
{
// Before showing login view check if user is authenticated.
// If they are redirect to suitable page,
// and print appropriate message
if (ControllerContext.HttpContext.User.Identity.IsAuthenticated)
{
// You can even use TempData as additionally, to hold data here and check in
//the view you redirect to if it is not null and is true ,
// then they are authenticated and show some message,
// e.g. You have been redirected here because you do not
// have authorization to view previous page.
TempData["UserAlreadyAuthicated"] = true;
return RedirectToAction("DashboardOrSomeOtherView");
}
// If they are not authenticated , show them login view
return View();
}
In the DashboardOrSomeOtherView
<div>
<h1>DashboardOrSomeOtherView</h1>
#{
if(TempData["UserAlreadyAuthicated"] != null && TempData["UserAlreadyAuthicated"].ToString().ToLower() == "true")
<div>You have been redirected here because of inadequate authorization</div>
}
</div>

unauthorizedRedirect set to false, still redirecting

I'm writing some REST api for my cake 3.0 application, and I need to set $this->Auth->unauthorizedRedirect to false, as the manual says that this would prevent my application to redirect to login url for unauthorized requests.
http://api.cakephp.org/3.0/class-Cake.Auth.BasicAuthenticate.html
The problem is that I'm trying to set it in my Users controller, and it doesn't work:
class UsersController extends AppController {
public function initialize() {
parent::initialize();
$this->loadComponent('RequestHandler');
}
public function beforeFilter(Event $event) {
parent::beforeFilter($event);
$this->Auth->allow(['logout']);
// Change the authentication mode when using REST api
if(! $this->RequestHandler->accepts('html')) {
$this->Auth->unauthorizedRedirect = false;
$user = $this->Auth->identify();
if ($user) {
$this->Auth->setUser($user);
}
}
}
This scripts works fine as detecting if a user is actually registered, but fails when I try to use wrong authentication data, showing the login form instead of throwing an error. What am I doing wrong?
Authentication and authorization are two different things
You are mixing up authentication and authorization, that's two different things. Logging in a user is authentication, testing whether a logged in user is allowed to access a specific action is authorization.
So the unauthorized redirect configuration applies to logged in users when accessing actions.
Handling unauthenticated requests
What you are looking for, ie throw an exception on unauthenticated requests, is done by the basic authentication adapter by default, so I assume that you actually aren't using this adapter!?
So if you are using a different adapter, this behavior is best implemented in either your controller where you are trying to identify the user
$user = $this->Auth->identify();
if (!$user) {
throw new ForbiddenException('Stop! Hammer time!');
} else {
$this->Auth->setUser($user);
}
or, in case you want the exception to be thrown for every controller, in a custom authentication adapters unauthorized() method, which is being invoked on unauthenticated requests before executing possible redirects. Quote from the docs:
Cookbook > Authentication > Handling Unauthenticated Requests
When an unauthenticated user tries to access a protected page first the unauthenticated() method of the last authenticator in the chain is called. The authenticate object can handle sending response or redirection by returning a response object, to indicate no further action is necessary. Due to this, the order in which you specify the authentication provider in authenticate config matters.
If authenticator returns null, AuthComponent redirects user to login action. [...]
Here's a simple example that extends the form authentication handler:
src/Auth/MyCustomAuthenticate.php
namespace App\Auth;
use Cake\Auth\FormAuthenticate;
use Cake\Network\Exception\ForbiddenException;
use Cake\Network\Request;
use Cake\Network\Response;
class MyCustomAuthenticate extends FormAuthenticate
{
public function unauthenticated(Request $request, Response $response)
{
if(!$request->accepts('text/html')) {
throw new ForbiddenException('Ah ah ah! You didn\'t say the magic word!');
}
}
}
Controller
$this->loadComponent('Auth', [
'authenticate' => [
'MyCustom'
]
]);
See also
Cookbook > Authentication > Creating Custom Authentication Objects
Cookbook > Authentication > Using Custom Authentication Objects

Yii Rights :: Not able to access controller action with defined Roles

I have successfully install Yii-Rights modules with Yii-Users.
I have:-
Create a Roles (Authenticated)
Give permission to certain controller.action (Campaign.index)
However, when i login to the system as "Authenticated" roles, (I'm sure rights is working fine in the login, since i redirect the user by the roles), the Campaign.index return Error 403 -> You are not authorized to perform this action.
However when "Admin" roles go into the controller it working just fine. I noticed there is a way to allow the user to enter the controller, with allowedaction(), but when i logout and enter the function, it displays. So, It is not a solution.
In my Controller, i have
class CampaignController extends RController {
/**
* #var string the default layout for the views. Defaults to '//layouts/column2', meaning
* using two-column layout. See 'protected/views/layouts/column2.php'.
*/
public $layout = '//client/dashboard';
/**
* #return array action filters
*/
public function filters() {
return array(
'rights', // perform access control for CRUD operations
//'postOnly + delete', // we only allow deletion via POST request
);
}
/**
* Specifies the access control rules.
* This method is used by the 'accessControl' filter.
* #return array access control rules
*/
public function accessRules() {
return array(
array('allow', // allow all users to perform 'index' and 'view' actions
'actions' => array('index', 'view', 'create', 'update'),
'users' => array('#'),
),
array('allow', // allow admin user to perform 'admin' and 'delete' actions
'actions' => array('admin', 'delete'),
'users' => array('admin'),
),
array('deny', // deny all users
'users' => array('*'),
),
);
}
I have read most of the web regarding to this topic, but i found none that have the same problem like i do. I have tried uninstall the module, and install back but no luck.
Im using Yii 1.1.14 and the latest Yii-rights downloaded from https://bitbucket.org/Crisu83/yii-rights/ (Last updated 2014-02-20)
Please help me define my problems.
Thank you. :)

User authentication using CodeIgniter

I have a problem creating authentication part for my application.
Below is the simplified version of my controllers.
The idea is that the MY_controller checks if session with user data exists.
If it doesn’t, then redirects to the index page where you have to log in.
MY_controller.php
class MY_Controller extends Controller {
function __construct()
{
parent::__construct();
$this->load->helper('url');
$this->load->library('session');
if($this->session->userdata('user') == FALSE) {
redirect('index');
} else {
redirect('search');
}
}
}
order.php - main controller
class Orders extends MY_Controller {
function __construct()
{
parent::__construct();
$this->load->helper('url');
$this->load->library('session');
}
function index()
{
// Here would be the code that validates information input by user.
// If validation is successful, it creates user session.
$this->load->view('header.html', $data); // load header
$this->load->view('index_view', $data); // load body
$this->load->view('footer.html', $data); // load footer
}
function search()
{
//different page
}
what is happening is that the browser is telling me that “The page isn’t redirecting properly. Firefox has detected that the server is redirecting the request for this address in a way that will never complete.”
It seems like the redirect() is being looped. I looked at a few other examples of user auth and they were build using similar technique.
When a user is already logged in, it appears you want to redirect them to /search/. The redirect occurs, and the constructor is called again, which recognizes that the user is already logged in, so it redirects them to /search/... you get the idea.
I would start by separating your login logic into it's own controller that doesn't extend from MY_Controller.
Also, note that when not logged in your controller redirects to 'index'. If the Index controller is also based on My_Controller, then it will redirect back to itself (until the user logs in and then Dolph Mathews' answer comes true).
You need to provide a 'safe zone' with no checking/redirecting that provides users with a login form (and note that your login controller/method has to have open access too!)
I tend to pop a gateway method into My_Controller, which is called only by private controllers/methods as required (in the constructor of completely private controllers). I'm sure there must be a better way though, perhaps like a gateway function in your My_Controller (as yours is done) but that filters for the URI path (e.g. allows index; index/login; index/logout etc)