Q : How can I show display different menu(s) by user role?
Description : the app has many roles. e.g HR manager, Account Manager, Operating Manager, Employee, Operator, ...., etc. I used rights and yii-user modules to create those roles. Those roles have different functions. So the app will show different menu for different user's role after logged in. Now, I can lock the function for different user. e.g when HR manger logged in, he/she can't route to other function of user role. But I don't know how to show the HR Menu for Hr Manager, only.
I am not a newbie for yii. but I'm a newbie for those modules (rihgts and yii-user).
If you're using RBAC you can set the 'visible' param of CMenu items depending on the users privileges, for example;
$this->widget('zii.widgets.CMenu',array(
'items'=>array(
array(
'label'=>'Home',
'url'=>array('site/index'),
),
array(
'label'=>'HR',
'url'=>array('/hr/index'),
'visible'=>Yii::app()->user->checkAccess('hr')
),
array(
'label'=>'Accounts',
'url'=>array('/account/index'),
'visible'=>Yii::app()->user->checkAccess('account')
),
array(
'label'=>'Operations',
'url'=>array('/operations/index'),
'visible'=>Yii::app()->user->checkAccess('operations')
),
),
);
This way users will only be able to see the items in the menu if they have access privileges for that area.
[EDIT]
As per simaremare's comment below, you can force caching of this query beyond the current request by extending CWebUser. Firstly, set your user to run through your new class (we'll call it TWebUser), so in your main.php config file;
'components'=>array(
'user'=>array(
...
'class'=>'TWebUser',
...
),
...
),
Now we need to create TWebUser to cache these beyond the current request (which is what CWebUser does (source code):
class TWebUser extends CWebUser
{
private $_access=array();
public function checkAccess($operation,$params=array(),$allowCaching=true)
{
if($allowCaching && $params===array() && isset($this->_access[$operation]))
return $this->_access[$operation];
$cache = Yii::app()->session['checkAccess'];
if($allowCaching && !$this->getIsGuest() && isset($cache[$operation]) && time() - $cache[$operation]['t'] < 1800)
{
$checkAccess = $cache[$operation]['p'];
} else {
$checkAccess = Yii::app()->getAuthManager()->checkAccess($operation,$this->getId(),$params);
if($allowCaching && !$this->getIsGuest())
{
$access = isset($cache) ? $cache : array();
$access[$operation] = array('p'=>$checkAccess, 't'=>time());
Yii::app()->session['checkAccess'] = $access;
}
}
return $this->_access[$operation] = $checkAccess;
}
}
Now your access results will be set for the whole session. This does mean that if you edit the RBAC permissions for a given account, they'll have to log out and log in again to see the new changes reflected in the browser.
I hope that helps! I'm sure I found this workaround from someone else (probably on SO), but I can't find the original post to give them credit.
You would wrap an if-statement around the menu-items that you may or may not want to show.
In the if-statement, you would have to test whether the user qualifies to see your menu-items.
For example:
<?php if (Yii::app()->user->isGuest): ?>
<?php $this->widget('zii.widgets.CMenu',array(
'items'=>array(
array(
'label'=>'this menu item visible only for Guests (not logged in)',
'url'=>array('/site/index')),
),
)); ?>
<?php endif; ?>
Related
I have custom user profile field Faculty.
I want to check it in the user login page, if the user is not assigned as Faculty returned to login page.
How can I do this?
You can use Moodle Events API to trigger an event while logging
The code will be something as the following
Inside your plugin directory create plugin/classes/observer.php file
Let's say your plugin is a clean theme, so the content of that file will be
class theme_clean_observer
{
public static function update_loggedin(\core\event\user_loggedin $loggedin){
//get user data by $loggedin
// check faculty value
// redirect to somewhere
}
}
And create plugin/db/events.php:
$observers = array(
array(
'eventname' => '\core\event\user_loggedin',
'callback' => '\theme_clean_observer::update_loggedin',
)
);
Simple, when the user logs in run a code that checks if the user is a faculty user or not. You can use the link function to direct them to the specific page you want.
for example:
If($UserType == "Faculty"){
link ( "Home.php" )
}else{
link ( "Login.php" )
}
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',
...
);
}
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));
}
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.
I have a concern about Zend Auth. I've searched throughout the internet, but haven't found any tutorial, article or discussion;
scenario: Login as 'ADMIN' on 'COMPUTER 01' and 'COMPUTER 02' concurrently.
I want my user login system to prevent the 'ADMIN' user from logging in on two computers at the same time. So that when a User is already logged in, the system disables login on another machine.
As far as I am aware this functionality is not built in to Zend_Auth, but you could achieve what you want by extending the Zend_Auth_Adapter that you are currently using and overiding the authenticate() method as danielrsmith suggests.
You would need to add a table to your DB that is set/unset by the login process. The problem is going to be the unset if the user does not specifically log out, but you could store a timestamp in the DB and allow the login to expire for the next login attempt.
My_Auth_Adapter_DbTable extends Zend_Auth_Adapter_DbTable
{
public function authenticate()
{
$authResult = parent::authenticate();
if($this->alreadyLoggedIn(){
$authResult = new Zend_Auth_Result(
Zend_Auth_Result::FAILURE_UNCATEGORIZED,
$this->_identity,
array('User already logged in')
);
} else {
$this->setLoggedIn();
}
return $authResult;
}
private function alreadyLoggedIn()
{
//check db table to see if $this->_identity is already logged in
//then return true or false as appropriate
}
private function setLoggedIn()
{
//update table in DB to reflect logged in status
}
}
I haven't tested this code, but it will, hopefully, get you started in the right direction.
Also, doing it this way will, I think, avoid the need to alter the session handler in any way.
Try this:
Create session table in database:
CREATE TABLE session (
id char(32),
modified int,
lifetime int,
data text,
PRIMARY KEY (id)
);
Store session in database by Zend_Session_SaveHandler_DbTable. Put the following code in bootstrap.php
protected function _initDoctrineSession()
{
$url=constant("APPLICATION_PATH").DIRECTORY_SEPARATOR ."configs".DIRECTORY_SEPARATOR."application.ini";
$config=new Zend_Config_Ini($url,"mysql");
$db=Zend_Db::factory($config->db);
Zend_Db_Table_Abstract::setDefaultAdapter($db);
$config = array(
'name' => 'session',
'primary' => 'id',
'modifiedColumn' => 'modified',
'dataColumn' => 'data',
'lifetimeColumn' => 'lifetime'
);
Zend_Session::setSaveHandler(new Zend_Session_SaveHandler_DbTable($config));
Zend_Session::start();
}
When log in, save user id or user name in session:
$logged_user = new Zend_Session_Namespace('logged_user');
$logged_user->logged_user = $user->name;
Another log in deletes all expired sessions in database firstly:
$sessionModel = new Session();
$sessionModel->removeExpiredSessions();
5.After log in, search session table records to see if current user already logged in:
$sessions = $sessionModel->querySessions();
$flag=true;
foreach ($sessions as $session){
if(strpos($session['data'], $user->name)>0 and strpos($session['data'],"Zend_Auth")>0
){
$flag=false;
}
}
if($flag == false){
$this->_forward('index','index');
return;
}
This will work. But there is a problem, if a user closes the Web browser before log out, the user will not be able to log in again before the session expired. Anyone can help to fix the last problem?