Cakephp3 Auth - allow some controllers to work without authoriziation - cakephp-3.0

Today i wanted to use Cakephp3 auth component.
I have studies cakephp3 blog tutorial and auth documentation.
I have followed Blog tutorial, and for now everything works fine, like login,logout etc...
I wanted to have UsersController to be protected and used only when i will logged in.
And this is ok, but now i saw that i need to login to other controllers actions, for example i have PagesController, which should be public.
I found this in docs:
// Allow only the view and index actions.
$this->Auth->allow(['view', 'index']);
But i have a lot of actions, and listing actions in this function may be problematic.
My question is: How can i globally set all actions of UsersController to be protected, and all the rest of controllers to be public ?
Thank You.

For UsersController
class UsersController extends AppController
{
public function beforeFilter(Event $event)
{
// allow only login, forgotpassword
$this->Auth->allow(['login', 'forgotpassword']);
}
}
For Other controller. (Example: PagesController)
class PagesController extends AppController
{
public function beforeFilter(Event $event)
{
// allow all action
$this->Auth->allow();
}
}

In your appsController, you can add below code. You must add all the view names in $this->Auth->allow('ViewName').
public function beforeFilter(Event $event)
{
parent::beforeFilter($event);
// Allow users to register and logout.
// You should not add the "login" action to allow list. Doing so would
// cause problems with normal functioning of AuthComponent.
$this->Auth->allow(['Index', 'View', 'Edit', 'CustomViewName']); // NOTE: DO NOT ADD VIEWS THAT ARE USED BY YOUR USERSCONTROLLER.
}
And from UsersController, you can remove $this->Auth->allow(['add', 'logout']);. This is how I would do it.

I think you can use ControllerAuthorize.It will allows you to handle authorization checks in a controller callback.Add this setting in your app component.
$this->loadComponent('Auth', [
'authorize' => 'Controller',
]);
Then you can prevent or allow access by isAuthorized()
example :
public function isAuthorized($user) {
if ( isset($user['role']) == 'yourRole' ) {
return TRUE;
}
else {
header("location: Router::url('/', true)");
exit();
}
}
For details
cake doc

Try:
$this->Auth->deny(['controllers name that you want to authorize']);

Related

Laravel 5.4: attach custom service provider to a controller

I created a service provider named AdminServiceProvider
namespace App\Providers;
use Modules\Orders\Models\Orders;
use Illuminate\Support\ServiceProvider;
use View;
class AdminServiceProvider extends ServiceProvider
{
public function boot()
{
$comments = Orders::get_new_comments();
View::share('comments', $comments);
}
public function register()
{
}
}
Registered the provider
App\Providers\AdminServiceProvider::class,
Now I try to attach it to the controller
namespace App\Http\Controllers\admin;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Providers\AdminServiceProvider;
class AdminController extends Controller
{
public $lang;
public function __construct()
{
}
public function index(){
return view('admin/dashboard');
}
}
Now I get this error message
Undefined variable: comments
This is the first time I try to use a custom service provider and don't know exactly how it works I'm sure there's something missing Hope you can help. Thanks in advance.
[UPDATE]
removed use App\Providers\AdminServiceProvider; from the controller
php artisan clear-compiled solved the problem but I want to attach it to some controllers not all controllers as the $comments are sent to all contollers in my app. So how to attach the service provider to specific controllers not all of them?
For the undefined variable run: php artisan clear-compiled will solve it
If you want to share a variable in some of your views you can create a middleware and assign it to the views you want to share the data with:
First create a middleware: php artisan make:middleware someName
Then in the handle function you add your view sharing logic:
$comments = Orders::get_new_comments();
view()->share('comments',$comments);
return $next($request);
Then register your middleware under the $routeMiddleware array and
give it an alias.
Then attach it to your routes like:
Route::group(['middleware'=> 'yourMiddlewwareName'], function(){
//your routes
});
If you have all your admin views in one directory (views\admin for example) you can use view composer in AdminServiceProvider:
public function boot()
{
view()->composer('admin.*', function($view){
$view->with('comments', Orders::get_new_comments());
});
}
It will attach comments variable to each view in your views\admin directory.
You can also attach a variable to some specific views or folders like this:
view()->composer(['admin.posts.*', 'admin.pages.index'], function($view){
$view->with('comments', Orders::get_new_comments());
});

Yii2 send message (or notification) to admin after any user make create or update

Three weeks ago I was trying to find a way to send message (or notification) to admin after any user make create or update, but ended up with nothing. I searched a lot and I did not find a clear solution, I am trying to understand Yii2 events, I found this link
http://www.yiiframework.com/wiki/329/real-time-display-of-server-push-data-using-server-sent-events-sse/
I think it is the key to solve my problem, but I am really stuck I don't know what to do, hope anyone can help me.
thanks
Consider using a behavior to handle this.
Assumptions
You have at least one model (possibly multiple) within your project.
You have a controller that contains at least two actions: actionCreate and actionUpdate.
An email is sent to an administrator whenever either of the aforementioned actions are called.
Events and Behaviours
When actionCreate is called a new record is inserted into the database through an instance of a model class that extends ActiveRecord. Similarly, when actionUpdate is called an existing record is fetched from the database, updated and saved back. An event (i.e: insert or update) is fired by the model (since model extends component and components are responsible for implementing events) on both of these occasions. Yii2 provides the ability to respond to these events using behaviours which "customize the normal code execution of the component”.
In short, this means you can bind custom code to any given event such that your code executes when the event is fired.
Proposed Solution
Now that we know a little something about events and behaviours, we could create a behaviour that executes some custom code whenever an insert or an update event is fired. This custom code could check the name of the action being called (is it called create or update?) in order to determine whether an email is required to be sent out.
The behaviour is useless on it’s own though, we would need to attach it to any models that should be triggering it.
Implementation of Solution
NotificationBehavior.php
<?php
namespace app\components;
use yii\base\Behavior;
use yii\db\ActiveRecord;
class NotificationBehavior extends Behavior
{
/**
* Binds functions 'afterInsert' and 'afterUpdate' to their respective events.
*/
public function events()
{
return [
ActiveRecord::EVENT_AFTER_INSERT => 'afterInsert',
ActiveRecord::EVENT_AFTER_UPDATE => 'afterUpdate',
];
}
/**
* This function will be executed when an EVENT_AFTER_INSERT is fired
*/
public function afterInsert($event)
{
// check the 'id' (name) of the action
if (Yii::$app->controller->action->id === 'create') {
// send email to administrator 'user performed insert'
}
}
/**
* This function will be executed when an EVENT_AFTER_UPDATE is fired
*/
public function afterUpdate($event)
{
if (Yii::$app->controller->action->id === 'update') {
// send email to administrator 'user performed update'
}
}
}
PostController.php
<?php
namespace app\controllers;
use Yii;
use app\models\Post;
use yii\web\Controller;
use yii\web\NotFoundHttpException;
class PostController extends Controller
{
/**
* Creates a new record
*/
public function actionCreate()
{
$model = new Post;
if ($model->load(Yii::$app->request->post()) && $model->save()) {
return $this->redirect(['view', 'id' => $model->id]);
} else {
return $this->render('create', [
'model' => $model,
]);
}
}
/**
* Updates an existing record
*/
public function actionUpdate()
{
// ...
}
}
Post.php (model)
<?php
namespace app\models;
use app\components\NotificationBehavior;
use yii\db\ActiveRecord;
class Post extends ActiveRecord
{
/**
* specify any behaviours that should be tied to this model.
*/
public function behaviors()
{
return [
// anonymous behavior, behavior class name only
NotificationBehavior::className(),
];
}
}
I would also advise checking out Yii2's TimestampBehavior implementation for a more concrete example.
Do you have a model to "user"? If yes, then just override method afterSave (it fires exactly after making any changes in the model) like this:
public function beforeSave($insert)
{
if (parent::beforeSave($insert)) {
// your notification logic here
return true;
}
return false;
}

YII2 Recaptcha issue

I am trying to use https://github.com/himiklab/yii2-recaptcha-widget for YII2 framework. Currently i am building a contact form, so i followed instruction there. However i faced a problem, by the instruction
public $reCaptcha;
public function rules()
{
return [
// ...
[['reCaptcha'], \himiklab\yii2\recaptcha\ReCaptchaValidator::className(), 'secret' => 'your secret key']
];
}
i have to add this in the model, but my contact form only exists in controller and view, i dont need a model to save the submission of feedback in database, so how can i do this verification of rules at controller layer?
You can try to use ad hoc validation for that.
$validator = new \himiklab\yii2\recaptcha\ReCaptchaValidator;
$validator->secret = '...';
if ($validator->validate($entered_recaptcha_code, $error)) {
// ok
} else {
echo $error;
}
I have not tried it before, some additional configuration might be needed.

Execute my code before any action of any controller

I would like to check if my user have filled certain fields in his profile before he can access any action of any controller.
For example
if(empty(field1) && empty(field2))
{
header("Location:/site/error")
}
In yii1 I could do it in protected\components\Controller.php in init() function
But in yii2 I'm not sure where to put my code. I cannot modify core files, but not sure what to do in backend of my advanced application to make it work.
I know I can user beforeAction() but I have too many controllers to do that and to keep track of every controller
In case you need to execute a code before every controller and action, you can do like below:
1 - Add a component into your components directory, for example(MyGlobalClass):
namespace app\components;
class MyGlobalClass extends \yii\base\Component{
public function init() {
echo "Hi";
parent::init();
}
}
2 - Add MyGlobalClass component into your components array in config file:
'components' => [
'MyGlobalClass'=>[
'class'=>'app\components\MyGlobalClass'
],
//other components
3 - Add MyGlobalClass into bootstarp array in config file:
'bootstrap' => ['log','MyGlobalClass'],
Now, you can see Hi before every action.
Please note that, if you do not need to use Events and Behaviors you can use \yii\base\Object instead of \yii\base\Component
Just add in config file into $config array:
'on beforeAction' => function ($event) {
echo "Hello";
},
Create a new controller
namespace backend\components;
class Controller extends \yii\web\Controller {
public function beforeAction($event)
{
..............
return parent::beforeAction($event);
}
}
All your controllers should now extend backend\components\Controller and not \yii\web\Controller. with this, you should modify every controller. I would go for this solution.
I believe you might also replace 1 class with another (so no change to any controller necessary), something like
\Yii::$classMap = array_merge(\Yii::$classMap,[
'\yii\web\Controller'=>'backend\components\Controller',
]);
See more details here: http://www.yiiframework.com/doc-2.0/guide-tutorial-yii-integration.html and I took the code from here: https://github.com/mithun12000/adminUI/blob/master/src/AdminUiBootstrap.php
you can put this in your index.php file. However, make sure you document this change very well as somebody that will come and try to debug your code will be totally confused by this.
Just i think this code on config file can help you:
'on beforeAction' => function ($event) {
// To log all request information
},
'components' => [
'response' => [
'on beforeSend' => function($event) {
// To log all response information
},
],
];
Or, https://github.com/yiisoft/yii2/blob/master/docs/guide/security-authorization.md use RBAC, to restrict access to controllers actions one at a time based on rules. Why would you want to restrict access to controller actions based on user fields is beyond me. You will not be able to access anything (including the login form) if you put a restriction there.

Zend FlashMessenger problems

I am new to Zend framework and I have a problem.
I created a controller abstract class which implements the functions like:
protected function AddError($message) {
$flashMessenger = $this->_helper->FlashMessenger;
$flashMessenger->setNamespace('Errors');
$flashMessenger->addMessage($message);
$this->view->Errors = $flashMessenger->getMessages();
}
protected function activateErrors()
{
$flashMessenger = $this->_helper->FlashMessenger;
$flashMessenger->setNamespace('Errors');
$this->view->Errors = $flashMessenger->getMessages();
}
So for each controller I am able to use
$this->AddError($error);
And then I render $error in layout.
So I want not to deal with flashMesenger in every controller.
but I have to execute the activateErrors when each action is executed.
for example
I have an controller test
class TestController extends MyController {
public function indexAction() {
$this->AddError("Error 1");
$this->AddError("Error 2");
$this->activateErrors();
}
public function index1Action() {
$this->AddError("Esdsd 1");
$this->AddError("sddsd 2");
$this->activateErrors();
}
}
Is there a way that I could execute this activateErrors in each action for every controller at the end of action without duplicating the code.
I mean I do not want to include this code at every action. Maybe there is a way to include it in my abstract class MyController.
Anybody any Idea?
thanks
What about using a postDispatch hook, in your parent MyController ?
Quoting that page :
Zend_Controller_Action specifies two
methods that may be called to bookend
a requested action, preDispatch() and
postDispatch(). These can be useful in
a variety of ways: verifying
authentication and ACL's prior to
running an action (by calling
_forward() in preDispatch(), the action will be skipped), for instance,
or placing generated content in a
sitewide template (postDispatch()).
Maybe this might do the trick ?
I actually contributed an enhancement to FlashMessenger which provides a lot of the functionality you're looking for.