how to exclude some routes when user is not logged in zfcuser? - php

So, i am new in Zend Framework, i installed ZfcUser and i want when the user is not logged to didn't access to some routes for example : /blog/article/add,
actually i use <?php if($this->zfcUserIdentity()) :?> to check if the user is logged but how can i redirect the user to my login route/user if is try to access to /blog/article/add,
so plz if someone has any idea i will be very appreciative :)

I disagree with the selected answer because it's not really practical to do these kind of things inside every action of every controller that you want to deny access to.
There are two big modules out there that are used for this task. The first one being BjyAuthorize and the other big one being ZfcRbac. Please check them out. ZfcRbac is my favorite (because i wrote documentation for it) but it requires PHP 5.4+

Simple Way:
in controller:
if (!$this->zfcUserAuthentication()->hasIdentity()) {
return $this->redirect()->toRoute('route_to_login',array('param' => $param));
}
Some more:
(after auth zfcuset will redirect you to previous controller)
in module.config.php
'controllers' => array(
'invokables' => array(
'zfcuser' => 'Application\Controller\UserController',
),
...
),
next copy file vendor\zf-commons\zfc-user\src\ZfcUser\Controller\UserController.php
to module Application (same folder as in module.config.php)
delete all function except authenticateAction
add:
use Zend\Session\Container;
and change
return $this->redirect()->toRoute($this->getOptions()->getLoginRedirectRoute());
to
$redirect_session = new Container('redirect');
$redirect = $redirect_session->redirect;
if($redirect!='')
{
unset($_SESSION['redirect']);
return $this->redirect()->toRoute($redirect, array('lang' => $lang));
}
return $this->redirect()->toRoute($this->getOptions()->getLoginRedirectRoute(),array('param' => $param));
and at last add in controller
use Zend\Session\Container;
...
if (!$this->zfcUserAuthentication()->hasIdentity()) {
$redirect_session = new Container('redirect');
$redirect_session->redirect = 'route_to_this_controller_action';
return $this->redirect()->toRoute('route_to_login',array('param' => $param));
}

Related

How to separate each action in a different file in Yii2

I'm new in Yii2 framework. To give structure to my web application, I want to put each controller in a subfolder and make a separate controller for each action in each subfolder. Like that one!
controllers
**User**
IndexController
EditController
UpdateController
**Profile**
IndexController
EditController
UpdateController
How can I arrange that in Yii2.
thanks in advance
Well your example is right.
controllers/user/IndexController.php
views/user/index/index.php
Then in IndexController/EditController/UpdateController you have actionIndex and if you run domain.com/user/index or domain.com/user/edit it will execute actionIndex in current controller (IndexController or EditController)
domain.com/user/index = domain.com/user/index/index
and
domain.com/user/edit = domain.com/user/edit/index
Not sure if there are other more effective ways, but one that works would be the following.
Note: This example assumes that you're using https://github.com/yiisoft/yii2-app-advanced but it can work for the basic app also, just changing the namespaces.
So, let's say you say we have a controller, and we want to store some of its actions into different php files.
<?php
// frontend\controllers\SiteController.php
namespace frontend\controllers;
use yii\web\Controller;
class SiteController extends Controller {
public function actions() {
return [
'error' => [
'class' => 'yii\web\ErrorAction',
],
'captcha' => [
'class' => 'yii\captcha\CaptchaAction',
'fixedVerifyCode' => YII_ENV_TEST ? 'testme' : null,
],
'hello-world' => [
'class' => 'frontend\controllers\site\HelloWorldAction',
],
];
}
public function actionIndex() {
// ...
}
So you can see we've got 3 external actions and one internal one.
The first two ones, are framework's tools for Error page and Captcha generation, actually they've inspired my answer.
And the third one, is defined by us:
'hello-world' => [
'class' => 'frontend\controllers\site\HelloWorldAction',
],
So we've named the action and we created our new action class into a separate directory.
<?php
// frontend\controllers\site\HelloWorldAction.php
namespace frontend\controllers\site;
use yii\base\Action;
class HelloWorldAction extends Action {
public function run($planet='Earth') {
return $this->controller->render('hello-world', [
'planet'=>$planet,
]);
}
}
And last, our view:
<?php
// frontend\views\site\hello-world.php
/* #var $this yii\web\View */
use yii\helpers\Html;
$this->title = 'Hello world page';
?>
<h1>Hello world!</h1>
<p>We're on planet <?php echo Html::encode($planet); ?></p>
And seeing it in action:
Update
After posting the answer I realized that maybe you could benefit from another technique also.
The previous answer is good if you want to do just that: Extract actions into individual files.
But, if your application will be of certain size, maybe you should consider using Modules.
You can create them manually or generate them with Gii:
And once generated, include it in your config:
<?php
......
'modules' => [
'profile' => [
'class' => 'frontend\modules\profile\Module',
],
],
......
Modules do just that, group application logic into one directory, controllers, models, views, components, etc.
Two more tips:
Now to access your module, simply visit http://www.your-site.local/profile/default/index, as you can see, it goes like module/controller/action.
And if you want to generate links to actions inside modules, you would do:
<?php
echo Url::to([
'profile/default/index',
'param'=>'value',
]);
?>
Again as you can see we're using module/controller/action as the route.
Last thing, if you're inside a module, let's say profile/picture/edit, and you want to link to Contact page from SiteController, you would do:
<?php
echo Url::to([
'//site/contact',
'param'=>'value',
]);
?>
Note the double slash // at the beginning of the route. Without it, it will generate the url to the current module profile/site/contact.

Preventing user from viewing another user profile in Yii

I have some question about my app. How can I prevent a user from viewing another user profile?
I mean, a user can only view their own profile. If user 123 is logged in, he is allowed in 'view&id=123' but denied in 'view&id=234' etc.
I know how to do this from the view page, but, am I able to deny it from my Controller? I'm thinking to use expression but I can't get it right. Here is my code
array('deny',
'actions'=>array('view'),
'expression'=>'$user->getName()!=??',
),
What is the right expression to replace the '??' part?
I tried to use $id (the actionView parameter) but it showed me an error..
I usually do this at controller level, I do something like this:
public function actionView($id){
if($id != Yii::app()->user->id) {
throw new CHttpException('403','You are not allowed to see this page');
}
//Continue with the code
}
Assumming that you've stored logged user id at the Yii:app()->user variable.
Also this might work:
array('deny',
'actions'=>array('view'),
'expression'=>'$_GET["id"] != Yii::app()->user->id',
),
Use option allow make it easier to get what you want.
'actions' => ['view', 'list-post', 'unsubscribe'],
'allow' => $_GET['id'] == Yii::$app->user->id,
The best practice is to create a custom filter (in application.components.controller, if you use it more than once):
public function filterCheckOwner($filterChain)
{
if ($id=Yii::app()->getRequest()->getParam('id'))
{
if($id != Yii::app()->user->id)
throw new CHttpException('403','You are not allowed to see this page');
else
$filterChain->run();
}
}
And then just add the actions that must run it in the filters() method with the same name (without 'filter'):
public function filters()
{
return array(
...
'checkOwner + view, update, whatever',
...
);
}

How to keep the main page as "index.php" while still Requiring log-in on other pages - yii

I'm running into some difficulty in using the added componenet requirelogin.php . This is the component that I've added
<?php
class RequireLogin extends CBehavior
{
public function attach($owner)
{
$owner->attachEventHandler('onBeginRequest', array($this, 'handleBeginRequest'));
}
public function handleBeginRequest($event)
{
if (Yii::app()->user->isGuest && !in_array($_GET['r'],array('site/login', 'site/index'))) {
Yii::app()->user->loginRequired();
}
}
}
?>
Note how 'site/index' is allowed in this as a page that I can visit.
Now, in my main.php I added the following
'behaviors' => array(
'onBeginRequest' => array(
'class' => 'application.components.RequireLogin'
)
),
Now, these two force me to go to site/login everytime - even though I have done as other stackoverflow answers have told me to and added
// sets the default controller action as the index page
'defaultController' => 'site/index',
Could anyone explain why this hasn't made my start page site/index?
_____________ ADDITION
I've also figured out that when I am going to the base action (i.e. mywebsite.com) it is NOT going to site/index. rather it is directly redirecting to site/login. This, however, does not occur, when I comment out the behaviors .
So, thank you darkheir for all of your work on this.
Ultimately, what happened was the $_GET['r] in the base path was simply '' because r was not point to anything in particular. As a result, when ever I looked in_array($_GET['r']) what I was getting in the very bae path was in_array('', array('site/login', 'site/account')) Now, unfortunately, '' is neither site/login or site/account so the page redirected using
Yii::app()->user->loginRequired();
The fix to this problem was
public function handleBeginRequest($event)
{
// note that '' is now one of the options in the array
if (Yii::app()->user->isGuest && !in_array($_GET['r'],array('site/login', 'site/index', '' ))) {
Yii::app()->user->loginRequired();
}
}

Yiiframework First time login

I'm currently busy with a project that needs users to go to a specific page to create a profile when they log in for the first time (and haven't created one yet). Honestly, I don't know where to start. I would like to do it in a good way.
So in short:
User signs up -> logs in -> needs to fill in form before anything else is allowed -> continue to rest of application
Question: What is a neat way to do this? A solution that isn't going to give me problems in the future development of the application.
I suggest you to use filters. In every controller where the completed profile is neeeded add this code:
public function filters() {
return array(
'completedProfile + method1, method2, method3', // Replace your actions here
);
}
In your base controller (if you don't use base controller, in any controllers) you need to create the filter named completedProfile with the simular code:
public function filterCompletedProfile($filterChain) {
$criteria = new CDBCriteria(array(
'condition' => 'id = :id AND firstname IS NOT NULL AND lastname IS NOT NULL',
'params' => array(':id' => Yii::app()->user->getId())
));
$count = User::model()->count($criteria);
if ($count == 1) {
$filterChain->run();
} else {
$this->redirect(array('user/profile'));
}
}
Possibly add a field to the user profile database table which denotes if they have filled out their profile information. Something like profile_complete. Then you can do a test on pages to see if profile_complete is true and display the page if so, and display the profile page if not.

Zend Framework 2 set global module variable

I want to make it so the variable "user" is global to all modules, so I've added this code
public function onBootstrap(MvcEvent $e)
{
$eventManager = $e->getApplication()->getEventManager();
$e->getViewModel()->setVariable('user',$e->getApplication()->getServiceManager()->get('auth_service')->getIdentity());
}
It works fine for the layout file, meaning that if I do var_dump($user) in the layout.phtml it will output the expected results, although in a view doing the same results in
Notice: Undefined variable: user in C:\webserver\apache\htdocs...
Any help on why this is happening? Am I doing something wrong?
Use Config-Variables to make stuff application-wide available.
// module.config.php
return array(
'someVariable' => 'someValue',
// all the other stuff
);
And all you have to to is access the config. Of course the access varies depending on where you're trying to access it, but ultimately it's done like this:
// Example for being inside any of your Controllers
$servoceLocator = $this->getServiceLocator();
$config = $serviceLocator->get('config');
$myValue = $config['someVariable'];
Hope it's clear enough.
Have you tried $this->user in view i think this should work.
In Zend Framework 2 there is a build in view helper called "Identity" that is available in all views. It requires that you register the service manager in a config.module.php like that:
'service_manager' => array(
...
'invokables' => array(
...
'Zend\Authentication\AuthenticationService' => 'Zend\Authentication\AuthenticationService',
),
)
and then in a view, you can access your registered "user" like this:
<?php echo $this->identity()?>
which will return your user. Here is a link to the zend documentation

Categories