How to programmatically recreate php yii session? - php

From my application view I need to programmatically logout current user and login another one right after that.
I want to login the second user into his own different CHttpSession (with another sessionID and so on). I need it for a security reasons.
How to implement this in Yii framework ?
Code below
$oSession->destroy();
$oSession->open();
doesn't work as expected..

looks like you are trying to impersonate users:
Create a function in your UserIdentity that would alow you to login as another known user:
protected function logInUser($user)
{
if($user)
{
$this->_user = $user;
$this->_id=$this->_user->id;
$this->setState('name', $this->_user->name);
$this->errorCode=self::ERROR_NONE;
}
}
In your controller, call this function to get the UserIdentity object and then use the Yii's CWebUser login
$ui = null;
$user = User::model()->findByPk($userId);
if($user)
{
$ui = new UserIdentity($user->email, "");
$ui->logInUser($user);
}
Yii::app()->user->login($ui, 0);
Remember to protect this controller's action from non authorized users.

A possible tricky way (tested):
session_unset();
Yii::app()->user->id = $the_new_id;
When the above code is executed, nothing visible happens on the page so you may want to redirect the browser:
$this->redirect('somewhere');
Upon the next page load, the user with the $the_new_id will be logged in

Related

Yii2 remote logout a user session from the user current session

I wanted to logout a user session logged in a different browser/environment from the current session of the same user. A feature similar to this - https://www.facebook.com/settings?tab=security&section=sessions&view.
Yii2 is the backend framework used. Using redis for session management - yii2-redis. I also save the session ids saved in database.
I followed this article -
http://www.codeinphp.com/general/php-code-snippets/remotely-destroy-different-session-php-forced-user-signout/
But didn't had any success.
session_id($old_session_id);
session_start(); // This line throws error.
session_destroy();
Deleting the key in redis using \Yii::$app->session->destroySession($oldSessionId) didn't logout.
Changing the session id to old one and then destroying the session also didn't work.
$currentSessionId = \Yii::$app->session->getId();
\Yii::$app->session->setId($oldSessionId);
\Yii::$app->getSession()->destroy();
\Yii::$app->session->setId($currentSessionId);
If anyone had success in implementing this successfully, please share your solution. Also if there is any documentation regarding this which can help, please do provide.
The first, session_start() must be call before session_id() and just call only once
if (session_status() == PHP_SESSION_NONE) {
session_start();
}
session_id($old_session_id);
session_destroy();
But just remove session, that is not enough if you allow user auto login because browser will auto login by using cookies. To solve, you must change user AuthKey - Yii2 use AuthKey to validate user auto login. By default each user have only one AuthKey in user table so that when you change AuthKey user logout anywhere. So we have to custom. Create for each user session an AuthKey, stored somewhere not in user table. Do it easy: extends yii\web\User class override afterLogin function to create AuthKey for each login session. override validateAuthKey function to validate auto login use our custom AuthKey. Now when you want to kill any user session : kill PHP session id and AuthKey that session will be logout right away.
I have been using this solution for my projects and it works fine.
Based on Ngo's answer, I figured out a method that works well and is easier to setup.
1) Add a "last_session_id" field to your user table.
2) Add following to your main controller:
public function afterAction($action, $result)
{
$result = parent::afterAction($action, $result);
if(Yii::$app->user->id)
{
//update the user table with last_session_id
$user = User::find()->where(['id' => Yii::$app->user->id])->one();
$user->last_session_id = Yii::$app->session->id;
$user->save(false);
}
return $result;
}
3) Change site/login action to following:
public function actionLogin()
{
if (!\Yii::$app->user->isGuest) {
return $this->goHome();
}
$model = new LoginForm();
if ($model->load(Yii::$app->request->post()) && $model->login()) {
//delete previous session ID and change auth key
Yii::$app->session->destroySession(Yii::$app->user->identity->last_session_id);
$u = \common\models\User::find()->where(['id' => Yii::$app->user->id])->one();
$u->auth_key = Yii::$app->security->generateRandomString();
$u->save(false);
return $this->goBack();
} else {
return $this->render('/site/login', [
'model' => $model,
]);
}
}

Is my CakePHP Session login secure?

I don't know how to use AuthComponent then this is the way I do user authentication with multiple roles is as follows:
There is 3 roles: Administrators, Resales and Clients.. one controller for each one, for individual views, and this is my beforeFilter for each Role/Controller:
AdministratorsController:
function beforeFilter(){
if (!$this->isAuth('Administrator'))
$this->redirect('/');
}
AppController:
function isAuth($strRole = NULL){
$data = $this->Session->read('User');
if (!$this->Session->check('User') || (!is_null($strRole) && $data['Role']['nome'] != $strRole))
return false;
return true;
}
In UsersController I do only authentication checking if $this->Session->read('User') exists, if the user exists, he gets all info and put in Session like this: $this->Session->write('User', $user); assuming that $user is the find from Model with all user information.
the question is, will I have problems? is that "right"? do not know if I was clear, if missing information, ask..
You're replicating logic the framework already implements for you.
See http://book.cakephp.org/2.0/en/tutorials-and-examples/blog-auth-example/auth.html#authorization-who-s-allowed-to-access-what
Taken from that page (you should still read it..):
public function isAuthorized($user) {
// Admin can access every action
if (isset($user['role']) && $user['role'] === 'admin') {
return true;
}
// Default deny
return false;
}
The user data gets passed to it, no need to fetch the user data yourself.
And if you ever have to fetch the user data use $this->Auth->user():
// Some field
$this->Auth->user('someField');
// Whole data
$this->Auth->user();
If the session key ever changes for some reason all you direct calls to the Session->read('User') will be in trouble. A good and not so unlikely example here is when the application has to be extended with a 2nd auth adapter that is not using the session. Auth will know about the data, Session won't have it.

Yii setFlash with Logout

Im tying to use the command sequence:
Yii::app()->user->setFlash('success', "Successful!");
Yii::app()->user->logout();
$this->redirect(array('user/login'));
The user got logged out and redirected, but the Setflash does not work.
I also tried to change the order of 2 frist commands, but got the same problem.
If I do not logout the user, the Setflash works fine.
How can I make both commands work?
this should work
Yii::app()->user->logout();
Yii::app()->session->open();
Yii::app()->user->setFlash(...);
If you need to destroy a whole session but you want to set a flash afterwards, you may extends CWebUser this way:
<?php
class BaseWebUser extends CWebUser
{
public function logout($destroySession = true)
{
parent::logout($destroySession);
Yii::app()->session->open();
}
}
?>
have a closer look here
I think you can use this :
public function afterLogout() {
// Create new session
$session=new CHttpSession;
$session->open();
// Set flash message
Yii::app()->user->setFlash('success', 'You are logged out successfully.');
// Prepare target URL after logout
$continue_url = Yii::app()->request->hostInfo . Yii::app()->createUrl('');
// Redirect
CController::redirect($continue_url);
}
Put it inside your WebUser components.
Flash messages are stored in the session. Logging the user our destroys the user's current session. Once session_destroy() is called, you must call session_start() again in order to generate a new session ID and have this work. Yii most likely does not do that.
If it's that important that you have a "Successful" message indicating that the logout worked - then redirect the user to a "logout successful" page. Alternatively, you can look into overriding the way Yii performs a logout - although I wouldn't recommend it.

Global access to variable

After the user has logged in I want to be able to save the userId for later use within the application. The only place in the application I retrieve the username is from the login form, through the login controller. However, that structure in my application is that the only thing that is passed to my master controller from the login controller is HTML.
Of course I could include the userId in a hidden field inside the HTML that's passed back to the master controller, but that seems too hacky.
So, is there a way that I can save a value (in this case the username) so that it's accessible from other classes/namespaces/functions whatever? I have read a bit about 'global', but haven't managed to get it work in my application.
from LoginController.php:
if ($loginView->TriedToLogin()){
$loginUsername = $loginView->GetUserName(); //Retrieved from form
$loginPassword = $loginView->GetPassword();
}
Upon login, you need to store your user token in a session.
See: http://au1.php.net/manual/en/features.sessions.php
Store user when logging in:
$_SESSION['user_id'] = 32; // fetch from your user provider
You can then write a class/function that utilises the session to check their login status and fetch their details when required.
Like so:
function getUserId()
{
return isset($_SESSION['user_id']) ? $_SESSION['user_id'] : false;
}
function isLoggedIn()
{
return isset($_SESSION['user_id']) && is_numeric($_SESSION['user_id']);
}
Then use anywhere in your application:
echo isLoggedIn() ? getUserId() : 'Anonymous';
Also, for great information on how to build an MVC framework, check out "Create your own framework... on top of the Symfony2 Components".
How about Sessions?
Session support in PHP consists of a way to preserve certain data
across subsequent accesses.
http://de2.php.net/manual/en/features.sessions.php
If it's only the username you want store, I would go with $_SESSION[].
It's not the most secure in a (shared) hosted environment, but it's so easy to call session_start(); first thing on pages using the stored data.

cakephp custom login conditions

I would like to check, whether user's account is activated while loggin in, but cake's Auth component takes care of the login in a way I don't know how to control. Cake basically uses blank login function and I have no idea how to check value of User.active.
Thanks in advance
The AuthComponent has a property for setting additional conditions just like this, called $userScope.
Just include this line in your beforeFilter() Auth setup block:
$this->Auth->userScope = array('User.active' => true);
Note: the above applies to Cake 1.x. For 2.x use:
$this->Auth->scope = array('User.active' =>true);
Then you can leave your login method blank and the AuthComponent will append this extra condition when authenticating the visitor.
You can see all the additional properties here:
http://book.cakephp.org/2.0/en/core-libraries/components/authentication.html#configuring-authentication-handlers
If you don't include this extra scope, then inactive users will still be able to log in and you'd have to log them out in your login() method after checking.
On your Users controller, or wherever you want to place it (the action that the login form links to):
function login() {
if ($this->Session->read('Auth.User')) {
$active = $this->Auth->user('active');
if ($active) {
//(do stuff)
}
else {
//(do other stuff)
}
}
}
This assumes that there is an "active" column in your User table that contains either true or false (or 1 or 0). $this->Auth->user() allows you to access the current logged in user's data. More information in here: http://book.cakephp.org/view/1264/user

Categories