How to get online duration of a user by using session? - yii

In yii I need to store the users _login time,logout time and total duration logged_ Using session.
I am new to session concept. I don know how to retrieve the total logged time, login time, log out time. In session table it stores only id--expire--data
My code for storing session is
protected/config/main.php
'components'=>array(
'session'=>array(
'class' => 'CDbHttpSession',
'connectionID' => 'db',
'sessionTableName' => 'dbsession',
),
),
All working fine but data stored in db are encrypted. So I am unable to retrieve the data I need. One more thing what data is stored in session table.
Alternative way I used without session but that also have problem. I used this for getting login time, logout time and total duration logged
I used a table activity with fields
id,username,user_activity,url,ip,time
Codes to fill datas in table when user login and logout is below
protected/controller/sitecontrollers.php
public function actionLogin()
{
/* log in */
$model=new LoginForm;
// if it is ajax validation request
if(isset($_POST['ajax']) && $_POST['ajax']==='login-form')
{
echo CActiveForm::validate($model);
Yii::app()->end();
}
// collect user input data
if(isset($_POST['LoginForm']))
{
$model->attributes=$_POST['LoginForm'];
// validate user input and redirect to the previous page if valid
if($model->validate() && $model->login())
{
$activity = new Activity;
$activity->username = Yii::app()->user->id;
//$activity->userId = Yii::app()->user->id;
$activity->user_activity = "1";
$activity->url=$_SERVER['HTTP_REFERER'];
$activity->ip=$_SERVER['REMOTE_ADDR'];
// This breaks the site when log in details are wrong
$activity->save();
if($activity->save())
$this->redirect(Yii::app()->homeUrl);
}
}
// display the login form
if(Yii::app()->user->id==null)
{
$this->render('login',array('model'=>$model));
}
else
{
throw new CHttpException(404,'User already logged in.');
}
}
/**
* Logs out the current user and redirect to homepage.
*/
public function actionLogout()
{
// log
$activity = new Activity;
$activity->username = Yii::app()->user->id;
$activity->user_activity = "0";
$activity->url=$_SERVER['HTTP_REFERER'];
$activity->ip=$_SERVER['REMOTE_ADDR'];
$activity->save();
Yii::app()->user->logout();
if($activity->save())
$this->redirect(Yii::app()->homeUrl);
}
In this way all workin fine but when browser closed the application is logged out and a data not entering in to the database.then the calculation of duration is getting wrong. So only I took the method session but don't know how to use.

i think you will never do such kind of thing corectly but with some mistake (more or less some minutes, depends on your needs).
1. Keeping alive with page requests
You could send alive status to your ajax page actionImAlive and more often you do that, more precisely you will tell how long user session was. If you do that alive thing 1 time in 5 minutes, you will have duration with ~5min mistake.
Add columns t_login, t_last_activity
on User login update t_login and t_last_activity with current timestamp
On every user new page load check if need to update t_last_activity
Main Controller *(components/Controller.php)
public function keepAlive( ) {
User::model()->updateByPk(
Yii::app()->user->id,
// array('t_last_activity' => 'NOW()') //CURRENT_TIMESTAMP
array('t_last_activity' => new CDbExpression('NOW()')) //CURRENT_TIMESTAMP..this working fine
);
}
// Runs after any action in controller
public function afterAction($action) {
self::keepAlive();
parent::afterAction($action);
}
// This action can be reached by ajax every 5min
// if user stays in one page long enaugh
// (write javascript..)
public function actionImAlive() {
self::keepAlive();
}
Above code updates t_last_activity every time user loads new page, but you could do it every 5min by saving last activity timestmap in user session and check it before executing keepAlive() function
Other controllers
All other controllers uses Controller class as parent
SiteController extends Controller
MyController extends Controller
SomeController extends Controller
2. For user display only (calculates duration from login time to now: now() - t_login)
If you want to display this session duration to user (why you should want to do this I don't know, but anyway..) you could just add t_login column to User table and calculate duration time on a fly.
These was top of my head.

Related

Yii2 Authentication not working as expected

I am working on a micro service. It has basically login and registration. I followed the Yii2 official guide. But now i am facing an issue. When i try to send request to the endpoints which are protected ( Only users with access_token can make request ) It works but very strange it checks all the rows in the database and if access_token is matches any rows in the database then it allows the request. But what i want - I am trying to get users information, if i pass the token i want only the information which belongs to current user ( Whose token is in request ) .
I am doing this in my UserController -
public function behaviors() {
$behaviors = parent::behaviors();
$behaviors['authenticator'] = [
'class' => HttpBearerAuth::className(),
];
$behaviors['authenticator']['only'] = ['view'];
return $behaviors;
}
And in User model have implemented this method -
public static function findIdentityByAccessToken($token, $type = null) {
return static::findOne(['access_token' => $token]);
}
Where i am doing wrong?
If the access_token in the request matches the access_token value for any of the users in the database, then, like you say, the authentication filter will forward the request to the corresponding action on your controller.
At this point the application user component points to the user that was found, you can use the user's component id to recover the record matching the request's user from the database.
// Get the user record
$current_user = Yii::$app->user->identity;
// Do something with the user record
return [
'username' => $current_user->username,
'last_update' => $current_user->updated_at
...
];

google account login for the first time displays error

I am trying to login the user with google account in my application. I am having a problem when the user is logged in for the first time with google account in my app it shows this error:
Argument 1 passed to Illuminate\Auth\Guard::login() must implement interface Illuminate\Auth\UserInterface, null given
In my controller I have this:
public function loginWithGoogle() {
// get data from input
$code = Input::get('code');
// get google service
$googleService = Artdarek\OAuth\Facade\OAuth::consumer("Google");
if (!empty($code)) {
// This was a callback request from google, get the token
$token = $googleService->requestAccessToken($code);
// Send a request with it
$result = json_decode($googleService->request('https://www.googleapis.com/oauth2/v1/userinfo'), true);
$user = User::whereEmail($result['email'])->first(['id']);
if (empty($user)) {
$data = new User;
$data->Username = $result['name'];
$data->email = $result['email'];
$data->google_id = $result['id'];
$data->first_name = $result['given_name'];
$data->last_name = $result['family_name'];
$data->save();
}
Auth::login($user);
return Redirect::to('/');
}
// if not ask for permission first
else {
// get googleService authorization
$url = $googleService->getAuthorizationUri();
// return to facebook login url
return Redirect::to((string) $url);
}
}
I know the problem is with Auth::login($user); as insert is performed at the same time with Auth::login($user); and it doesn't find data from database for the first time, but I don't know how to avoid this error and instead redirects to the main page even when the user is logged in for the first time. After this error the user is logged in, but how to avoid this?
Without knowing whether the rest of the code works, you definitely have a problem here:
if (empty($user)) {
$data = new User;
(...)
$data->save();
}
Auth::login($user);
When you're done creating your user, the $user variable is still empty. Your user is actually called $data. You should either rename the variable, or do the login with $data instead. Hopefully, that's enough to make the code work. :)

viaRemember not work - laravel

Auth :: attempt works perfect, but when you pass the second parameter "true" apparently does not care or does not recover with viaRemember
viaRemember fails to work, check this
controller User
`$`userdata = array(
'email' => trim(Input::get('username')),
'password' => trim(Input::get('password'))
);
if(Auth::attempt(`$`userdata, true)){
return Redirect::to('/dashboard');
}
view 'dashboard', always show 777
#if (Auth::viaRemember())
{{666}}
#else
{{777}}
#endif
I have hit the same obstacle, so looking into the code one can see that viaRemember is not meant to be used as a function to check if the user was logged into the system in one of all the ways a user can be logged in.
'viaRemember' is meant to check if a user was logged into the system specifically via the `viaRemember' cookie.
From what I gather, authentication of user is remembered in two ways:
a via remember cookie.
The cookie value is compared to the via remember field in the users table.
a session cookie.
The cookie value is used in the server to get the session from the
session store. On the session object from the store there is data attached. One of the
data items is the user id connected to the session. The first time
the session was created, the system attached the user id to the data
of the season.
In Illuminate\Auth\Guard class:
public function user()
{
if ($this->loggedOut) return;
// If we have already retrieved the user for the current request we can just
// return it back immediately. We do not want to pull the user data every
// request into the method because that would tremendously slow an app.
if ( ! is_null($this->user))
{
return $this->user;
}
$id = $this->session->get($this->getName());
// First we will try to load the user using the identifier in the session if
// one exists. Otherwise we will check for a "remember me" cookie in this
// request, and if one exists, attempt to retrieve the user using that.
$user = null;
if ( ! is_null($id))
{
$user = $this->provider->retrieveByID($id);
}
// If the user is null, but we decrypt a "recaller" cookie we can attempt to
// pull the user data on that cookie which serves as a remember cookie on
// the application. Once we have a user we can return it to the caller.
$recaller = $this->getRecaller();
if (is_null($user) && ! is_null($recaller))
{
$user = $this->getUserByRecaller($recaller);
}
return $this->user = $user;
}
The getUserByRecaller function is called only if the session cookie authentication did not work.
The viaRemember flag is only set in the getUserByRecaller function. The viaRemember method is only a simple getter method.
public function viaRemember()
{
return $this->viaRemember;
}
So in the end, we can use Auth::check() that does make all the checks including the viaRemember check. It calls the user() function in the Guard class.
It seems also the viaRemember is only an indicator. You need to do a type of Auth::check() the will get the process of authentication started and so the user() function will be called.
It seems that your project is on Laravel 4.0 but viaRemember() is added in Laravel 4.1! So that's expected.
in config\session.php file change the 'expire_on_close' = false to true and once you close restart your browser, it must be ok.

Auth::check() and before('auth') = always sql request for every page?

Am I messing with something or does Laravel conduct sql request to grab data from user table even if all i do on specific page is Auth::check() or have before('auth') filter in router? It can not "get" that user is logged in just from session data (login_82e5d2c56bdd0811318f0cf078b78bfc = user_id etc) without
select * from `users` where `id` = ? limit 1
?
There is some security or other risen to not deal just with session data in case of simple checking of user status (i would prefer to make 1 request per session (not per page) to mysql for user data (or else) and than store it in session, and session set to store in memcached or redis and i am wondering can i get that "out of the box"\without serious changes in Authentication system of framework)?
You can keep it's instance in the container when you check it for the first time
App::before(function($request)
{
App::singleton('myApp', function(){
$app = new stdClass();
if(Auth::check()) {
$app->user = Auth::User();
$app->isLogedin = TRUE;
}
else {
$app->user = false;
$app->isLogedin = false;
}
return $app;
});
$myApp = App::make('myApp');
View::share('myApp', $myApp);
});
Now in every view, you can check like this
#if($myApp->isLogedin)
Hello {{ $myApp->user->user->user_name }}!
#endif
In any controller, you can use it like
$myApp = App::make(myApp); // Get it from the container
// do whatever you want to do
So, you can check logged in or not and also can use Auth::user() object using $myApp->user.

How to make entry of user when password is wrong

In yii i am creating login module. I want to give access to users only if username and passwords are correct and need to store this user's id into 'Success' table. But when only username is correct and password is wrong,i want to store that user's id into 'attempt' table in order to give only 3 chance to him for entering correct password. But when i am implementing this,when password is wrong entry doesnt get enterd into attempt table. i had created ActionLogin() method as-
public function actionLogin()
{
$model=new LoginForm;
$command = Yii::app()->db->createCommand();
// if it is ajax validation request
if(isset($_POST['ajax']) && $_POST['ajax']==='login-form')
{
echo CActiveForm::validate($model);
Yii::app()->end();
}
// collect user input data
if(isset($_POST['LoginForm']))
{
$model->attributes=$_POST['LoginForm'];
// validate user input and redirect to the previous page if valid
if ($model->validate())
{
if($model->login()) {
$command->insert('attempt', array(
'id'=>Yii::app()->user->getId(),
));
$this->redirect(Yii::app()->user->returnUrl);
}
if(!$model->login())
{
$command->insert('attempt', array(
'id'=>Yii::app()->user->getId(),
));
}
}
}
// display the login form
$this->render('login',array('model'=>$model));
}
What changes i need to do in order to make entry of user's id into attempt table when password is wrong. Please help me.
As stated in the comments:
A user that is visiting a Yii based website doesn't have a user id assigned, only logged in users have a userId assigned (which makes sense...).
The best approach would be to do IP based checks on the login attempts. You can retrieve a user his IP by the following piece of code:
Yii::app()->request->userHostAddress
Also credits for this answer to #Stu and #Nikos-Tsirakis