Action after loginByCookie() - yii2

In my app, I'm using session to store some user's data (e.g. name, role). I'm using default User class on common/models from yii2 and I'm getting the data I need by user id.The problem is that the data I stored in session are lost if user resume their previous session without entering password (using remember me option). To solve this, I tried to modify and put the codes for saving user's data inside validateAuthKey() method in User since it's called when yii2 checking for authkey to resume session. As in:
public function validateAuthKey($authKey)
{
$user = Member::findOne(['account_id' => Yii::$app->user->id]);
// Save on session
Yii::$app->session->set('name', $user->first_name . ' ' . $user->last_name);
Yii::$app->session->set('position', $user->position);
// .. other data
return true;
}
As you can see, I use the id of logged user to find Member. But by doing this, I got "Trying to get property of non-object" exception from Yii::$app->user->id. I can only get the id after the login process is completed. After prying around, I found that loginByCookie() from User in vendor\yiisoft\yii2\Web is used to log in. Is there any way to save user's data without tampering codes on vendor? Maybe some kind of event after loginByCookie() is executed.

Create your own User model extends \yii\web\User
namespace frontend\components;
class User extends \yii\web\User
{
protected function afterLogin($identity, $cookieBased, $duration)
{
...Do something here...
parent::afterLogin($identity, $cookieBased, $duration);
}
}
Modify the config (main.php).
'components' => [
'user' => [
'class' => 'frontend\components\User',
'identityClass' => 'common\models\User',
...
]
]

Put the below code in common/config/bootstrap.php
The EVENT_AFTER_LOGIN will be called even if the user is logged in by cookie.
use yii\web\User;
use yii\base\Event;
Event::on(User::className(), User::EVENT_AFTER_LOGIN, function() {
$user = Member::findOne(['account_id' => Yii::$app->user->id]);
// Save on session
Yii::$app->session->set('name', $user->first_name . ' ' . $user->last_name);
Yii::$app->session->set('position', $user->position);
});

Related

Laravel: Store error messages in database

Any one know how to send error messages to database in laravel which generate from app/exceptions/handler.php ?
I need to send what error massages generated in report() method to database.
If you are interested doing this manually, you can do something as following.
Step 1 -
Create a model to store errors that has a DB structure as following.
class Error extends Model
{
protected $fillable = ['user_id' , 'code' , 'file' , 'line' , 'message' , 'trace' ];
}
Step 2
Locate the App/Exceptions/Handler.php file, include Auth, and the Error model you created. and replace the report function with the following code.
public function report(Exception $exception) {
// Checks if a user has logged in to the system, so the error will be recorded with the user id
$userId = 0;
if (Auth::user()) {
$userId = Auth::user()->id;
}
$data = array(
'user_id' => $userId,
'code' => $exception->getCode(),
'file' => $exception->getFile(),
'line' => $exception->getLine(),
'message' => $exception->getMessage(),
'trace' => $exception->getTraceAsString(),
);
Error::create($data);
parent::report($exception);
}
(I am demonstrating this using laravel 5.6)
Because Laravel uses Monolog for handling logging it seems that writing Monolog Handler would be the cleanest way.
I was able to find something that exists already, please have a look at monolog-mysql package. I did not use it, so I don't know whether it works and if it works well, but it's definitely good starting point.

The default remember_me token in Laravel is too long

TL;DR How can I use my own way of generating the remember_me token?
I have an old site, written without any framework, and I have been given the job to rewrite it in Laravel (5.4.23). The DB is untouchable, cannot be refactored, cannot be modified in any way.
I was able to customise the Laravel authentication process using a different User model, one that reflect the old DB. But when it comes to the "Remember me" functionality, I have an issue with the length of the token.
The old site already uses the "Remember me" functionality but its DB field has been defined as BINARY(25). The token generated by the SessionGuard class is 60 characters long.
My first attempt was to try and find a way to shorten the token before writing it into the DB, and expand it again after reading it from the DB. I couldn't find such a way (and I'm not even sure there is such a way).
Then I looked into writing my own guard to override the cycleRememberToken (where the token is generated). I couldn't make it work, I think because the SessionGuard class is actually instantiated in a couple of places (as opposed to instantiate a class based on configuration).
So, I am stuck. I need a shorten token and I don't know how to get it.
Well, I was on the right track at one point.
I had to create my own guard, register it and use it. My problem, when I tried the first time, was that I did not register it in the right way. Anyway, this is what I did.
I put the following in AuthServiceProvides
Auth::extend('mysession', function ($app, $name, array $config) {
$provider = Auth::createUserProvider($config['provider']);
$guard = new MyGuard('lrb', $provider, app()->make('session.store'));
$guard->setCookieJar($this->app['cookie']);
$guard->setDispatcher($this->app['events']);
$guard->setRequest($this->app->refresh('request', $guard, 'setRequest'));
return $guard;
});
I change the guard in config/auth.php as
'guards' => [
'web' => [
'driver' => 'mysession',
'provider' => 'users',
],
'api' => [
'driver' => 'token',
'provider' => 'users',
],
],
and finally my new guard
class MyGuard extends SessionGuard implements StatefulGuard, SupportsBasicAuth
{
/**
* #inheritdoc
*/
protected function cycleRememberToken(AuthenticatableContract $user)
{
$user->setRememberToken($token = Str::random(25));
$this->provider->updateRememberToken($user, $token);
}
}

How to use Url::remember in yii2

I want to create a link on my error page to take user back to the previous link.
Suppose the current URL is http://example.com/site/product, and a user try to view
http://example.com/site/product?id=100 and a product with id =100 does not exit, the system should throw 404 error to the error page, now if i want to create a link to take the user back to http://example.com/site/product the previous URl how do I make this work. i can make this work by hardcoding this in my error views file, but i want it dynamically as i have many controller an action using the same view file.
I try this in my site conteoller
controller/site
public function actions()
{
$url = Url::remember();
return [
'error' => [
'class' => 'yii\web\ErrorAction',
],
$this->render('error',['url'=>$url]),
];
}
and try to get the value the in error view file like this
/views/site/error.php
<p>
<?= Html::a('go back', [$url)?>
</p>
but it has no vaule..
please any good idea on how to make this work, am also open to new solution
this is form Yii2 Guide http://www.yiiframework.com/doc-2.0/guide-helper-url.html#remember-urls
There are cases when you need to remember URL and afterwards use it
during processing of the one of sequential requests. It can be
achieved in the following way:
// Remember current URL Url::remember();
// Remember URL specified. See Url::to() for argument format.
Url::remember(['product/view', 'id' => 42]);
// Remember URL specified with a name given
Url::remember(['product/view', 'id' => 42], 'product');
In the next
request we can get URL remembered in the following way:
$url = Url::previous();
// or
$productUrl = Url::previous('product');

Using Dancer2::Plugin::DBIC to pull values from database

I have a webapp where a user can log in and see a dashboard with some data. I'm using APIary for mock data and in my Postgres Database each of my users have an ID. These ID's are also used in the APIary JSON file with relevant information.
I'm using REST::Client and JSON to connect so for example the url for the user's dashboard is: "/user/dashboard/12345" (in Apiary)
and in the database there is a user with the ID "12345".
How can I make it so when the user logs in, their ID is used to pull the data that is relevant to them? (/user/dashboard/{id})? Any documentation or advice would be much appreciated!
The docs of Dancer2::Plugin::Auth::Extensible are showing one part of what you need to do already. In short, save the user ID in the session. I took part of code in the doc and added the session.
post '/login' => sub {
my ($success, $realm) = authenticate_user(
params->{username}, params->{password}
);
if ($success) {
# we are saving your user ID to the session here
session logged_in_user => params->{username};
session logged_in_user_realm => $realm;
} else {
# authentication failed
}
};
get '/dashboard' => sub {
my $client = REST::Client->new();
# ... and now we use the user ID from the session to get the
# from the webservice
$client->GET( $apiary . '/user/dashboard/' . session('logged_in_user') );
my $data = $client->responseContent();
# do stuff with $data
};
For those who want to know what I ended up doing:
Dancer2::Plugin::Auth::Extensible has
$user = logged_in_user();
When I printed this it showed me a hash of all the values that user had in the database including the additional ID I had. So I accessed the id with
my $user_id = $user->{user_id};
And appended $user_id to the end of the url!

How to update the changed username in session variable without logout or session destroy

Question:
How to update the changed username in session variable without logout or session destroy ?
For Example:
I am login with username "Ram" and this username storing in session variable User_Name,after logged in i am changing my username "Ram" into "Kumar". So this newly changed username should get updated in session variable User_Name automatically without logout from my account.
Sample Controller Code for Login:
function check_database($password)
{
// Field validation succeeded. Validate against database
$username = $this->input->post('username');
// query the database
$result = $this->civic_soft_model->login($username, $password);
if($result)
{
$sess_array = array();
foreach($result as $row)
{
$sess_array = array(
'UID' => $row->UID,
'User_Name' => $row->User_Name,
'User_Type' => $row->User_Type,
'User_OTP' => $row->User_OTP
// 'Login_Status' => $row->Login_Status
// 'Node_Id' => $row->Node_Id
);
$this->session->set_userdata('logged_in', $sess_array);
}
return TRUE;
}
else
{
$this->form_validation->set_message('check_database', 'Invalid username or password');
return false;
}
}
NOTE:
I am using PHP,MySQL and CodeIgniter MVC Framework.
Please Help Me Friends...
I actually see what the problem is now. You are setting 'logged_in' to be an array. Not sure if that's common, but what I usually do is set 'logged_in' as a boolean and I set userdata the data that I need in another array.
However, for you case you can try this:
$newUserData = $this->session->userdata('logged_in');
if (is_array($newUserData)) {
$newUserData['User_Name'] = $new_username;
$this->session->set_userdata('logged_in', $newUserData);
}
For better usability, I would addd a function to $this->civic_soft_model called "updateUser" or something of that nature. And when that function is called, you can update all of the session data that you need to.
Insert this function to your controller and call it whenever there is an update made on the users information.
//$new_username : the username inputted by user when he is trying to update his account
function update_session($new_username){
$this->session->set_userdata('User_Name', $new_username);
}