Extended Session in L5 does not persist between requests - php

I am extending the session provider in order to persist some required data. I started editing AppServiceProvider's boot method:
\Session::extend('desk', function($app)
{
return new Desk();
});
Desk class looks like:
namespace App\Services;
use Illuminate\Session\ExistenceAwareInterface;
class Desk implements \SessionHandlerInterface, ExistenceAwareInterface{
/**
* The existence state of the session.
* #var bool
*/
protected $exists;
public function close()
{
return true;
}
public function destroy($session_id)
{
$session = $em->find('Session', $session_id);
$em->remove($session);
$em->flush();
return true;
}
public function gc($maxlifetime)
{
// TODO: Implement gc() method.
}
public function open($save_path, $session_id)
{
return true;
}
public function read($session_id)
{
$session = $em->find('Session', $session_id);
if ($sesion !== null){
$this->exists = true;
return $session->getPayload();
}
}
public function write($session_id, $session_data)
{
$session = $em->find('Session', $session_id);
if ($session === null){
$session = new Session($session_id, $session_data);
$em->persist($session);
}
else{
$session->setPayload($session_data);
}
$em->flush();
$this->exists = true;
}
public function setExists($value)
{
$this->exists = $value;
return $this;
}
}
After finish the implementation, I changed the session config to this:
return [
'driver' => 'desk',
'lifetime' => 120,
'expire_on_close' => false,
'encrypt' => false,
'files' => storage_path().'/framework/sessions',
'connection' => null,
'table' => 'sessions',
'lottery' => [2, 100],
'cookie' => 'lote_session',
'path' => '/',
'domain' => null,
'secure' => false,
];
When I load the page there is not problem, but after do a success login request and then, refresh the page, the session expires and the user is a guest again. Do I miss something?
Additional information: if I revert the session driver to "file", everything goes fine.

Well, for others that need/want to extends the session provider like me, pay attention to session's table structure. My mistake was that the payload column was set as:
payload varchar(255) not null
Because of laravel serialization data, the payload value could be has more than 255 characters length, in consequence, it will break the data and make it inconsistent. You can consider:
payload text not null

Related

Symfony - test properly voter class

I have trouble writing test for my custom voter in Symfony as I am new at writing test:
This is part of the code I want to test:
protected function voteOnAttribute(
string $attribute,
$subject,
TokenInterface $token
): bool
{
$user = $token->getUser();
if (!$user instanceof UserInterface) {
return false;
}
return match ($attribute) {
'read' => $this->canRead($member, $subject),
'create' => $this->canCreate($member, $subject),
default => throw new Exception(sprintf('Unhandled attribute "%s"', $attribute))
};
}
And this is my test class:
protected $token;
public function setUp(): void
{
$this->token = $this->createMock(TokenInterface::class);
$this->token
->method('getUser')
->willReturn([
'fistName' => 'Jonh',
'lastName' => 'Doe',
'email' => 'johndoe#socialhp.com'
]);
}
/**
* #dataProvider provideCases
*/
public function testVote(
array $attributes,
string $subject,
?TokenInterface $token,
$expectedVote
) {
$voter = new RoleVoter();
$this->assertEquals($expectedVote, $voter->vote($this->token, $subject, $attributes));
}
public function provideCases(): \Generator
{
yield 'user cannot read' => [
['read'],
'customers',
$this->token,
VoterInterface::ACCESS_DENIED,
];
yield 'user can read' => [
['read'],
'customers',
$this->token,
VoterInterface::ACCESS_GRANTED,
];
}
And I always get:
testVote with data set "user can read" (array('read'), 'customers',
null, 1) Failed asserting that -1 matches expected 1.
I would really appreciate if someone would help me continue with this.. Thanks
You didn't stubbed this method $token->getUser().
As you're defining $token as a test double, a stub to be accurate, you're somehow forced to describe every interaction with it, otherwise the testing framework (should be phpunit in this case, if I'm not mistaking), will return null for every not-defined interaction (method call).
By default, all methods of the original class are replaced with a dummy implementation that returns null (without calling the original method). Using the will($this->returnValue()) method, for instance, you can configure these dummy implementations to return a value when called.
(from docs)
You need to write something like
$this->token
->method('getUser')
->willReturn($user);
where $user is another test double you need to create.
As a conclusion, I suggest to test also the case where $token->getUser() returns null (that's basically what you're already doing here, inadvertently

Save session/cookies like discord in php

I would like to save sessions and cookies as discord does. What i mean? I mean when user sign in to discord account on browser and delete all cookies/session on browser by clicking padlock and cookies. When that will deleted everytime session file is created. And when i refresh site i was still logged on account. I want do something on this same way but when i use session, cookies, Header (to header i cant add expires date) And delete cookies by this same way it not adding that again because my script cant get any information about user. I thinking to do a JavaScript while to add every second sessionstorage or localstorage. And check that everytime when user open site but that is not good for optimalization. So anyone had idea how to do that?
Update
CreateInfo.php
<?php
namespace Client\Info;
use Client\Info\CheckInfo;
use Client\Info\SetInfo;
class CreateInfo
{
public function __construct()
{
$this->checkInfo = new CheckInfo();
$this->setInfo = new SetInfo();
$this->status = $this->checkInfo->getStatus();
}
public function control()
{
if($this->status['session'] && $this->status['cookie'] && $this->status['sameCookieSession']){
if(!$this->checkInfo->checkIpStatus()){
$this->setInfo->addIp(true, true, true);
}
}else if($this->status['session'] && $this->status['cookie'] && !$this->status['sameCookieSession']){
$this->setInfo->addCookie(true);
if(!$this->checkInfo->checkIpStatus()){
$this->setInfo->addIp(true, true, false);
}
}else if($this->status['session'] && !$this->status['cookie']){
$this->setInfo->addCookie(true);
if(!$this->checkInfo->checkIpStatus()){
$this->setInfo->addIp(true, false, false);
}
}else if(!$this->status['session'] && $this->status['cookie']){
$this->setInfo->addSession(true);
if(!$this->checkInfo->checkIpStatus()){
$this->setInfo->addIp(false, true, false);
}
}else{
$this->setInfo->setAll();
}
}
public function run()
{
$this->control();
}
}
CheckInfo.php
<?php
namespace Client\Info;
use Client\Info\InfoDatabase;
use Client\Cookie\CookieFunction;
use Client\Session\SessionFunction;
use Client\Ip\IpFunction;
class CheckInfo
{
public function __construct()
{
$this->infoDatabase = new InfoDatabase();
$this->cookieFunction = new CookieFunction();
$this->sessionFunction = new SessionFunction();
$this->ipFunction = new IpFunction();
$this->session = $this->sessionFunction->getSession('client');
$this->cookie = $this->cookieFunction->getCookie('client');
}
public function checkExist($data)
{
if(!isset($data) || empty($data)){
return false;
}
if(!$this->infoDatabase->checkExistInDb($data)){
return false;
}
return true;
}
public function getStatus()
{
if($this->checkExist($this->cookie)){
if($this->checkExist($this->session)){
if($this->cookie == $this->session){
return[
'session' => true,
'cookie' => true,
'sameCookieSession' => true
];
}else{
return[
'session' => true,
'cookie' => true,
'sameCookieSession' => false
];
}
}else{
return[
'session' => false,
'cookie' => true,
'sameCookieSession' => false
];
}
}else{
if($this->checkExist($this->session)){
return[
'session' => true,
'cookie' => false,
'sameCookieSession' => false
];
}else{
return[
'session' => false,
'cookie' => false,
'sameCookieSession' => false
];
}
}
}
public function checkIpStatus()
{
$ip = $this->ipFunction->getIp();
$result = false;
if($this->getStatus()['session']){
$result = $this->infoDatabase->checkExistIpInDb($this->session, $ip);
}else if($this->getStatus()['cookie']){
$result = $this->infoDatabase->checkExistIpInDb($this->cookie, $ip);
}
return $result;
}
}
SetInfo.php
<?php
namespace Client\Info;
use Client\Info\InfoDatabase;
use Client\Cookie\CookieFunction;
use Client\Session\SessionFunction;
use Client\Ip\IpFunction;
use Client\Currency\CurrencyFunction;
use App\Element\Random\RandomString;
class SetInfo
{
public function __construct()
{
$this->infoDatabase = new InfoDatabase();
$this->cookieFunction = new CookieFunction();
$this->sessionFunction = new SessionFunction();
$this->ipFunction = new IpFunction();
$this->currencyFunction = new CurrencyFunction();
$this->randomString = new RandomString();
$this->cookie = $this->cookieFunction->getCookie('client');
$this->session = $this->sessionFunction->getSession('client');
}
public function addIp($session, $cookie, $sameCookieSession)
{
$ip = $this->ipFunction->getIp();
if($sameCookieSession){
$this->infoDatabase->addIp($this->cookie, $ip);
}else{
if($session){
$this->infoDatabase->addIp($this->session, $ip);
}else if($cookie){
$this->infoDatabase->addIp($this->cookie, $ip);
}
}
}
public function addCookie($session)
{
if($session){
$this->cookieFunction->setCookie('client', $this->session);
}
}
public function addSession($cookie)
{
if($cookie){
$this->sessionFunction->setSession('client', $this->cookie);
}
}
public function setAll()
{
$rand = $this->randomString->generate(128);
$ip = $this->ipFunction->getIp();
$currency = $this->currencyFunction->getCurrencyCode();
$this->infoDatabase->addCookie($rand);
$this->infoDatabase->addIp($rand, $ip);
$this->infoDatabase->addCurrency($rand, $currency);
$this->cookieFunction->setCookie('client', $rand);
$this->sessionFunction->setSession('client', $rand);
}
}

How to use new microsoft graph api to log users in to yii site

I'm creating a site that needs an oauth authorization through microsoft. In yii/authclient there's only live client and it is not working anymore.
I tried to write my own but something goes wrong. As far as I understood my AuthAction doesn't see clientId and returns 404 exception without text. Here's my code of the auth client.
What I get
AuthAction class method run (it's default)
class Office365OAuth extends OAuth2
{
public $authUrl = 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize';
public $tokenUrl = 'https://login.microsoftonline.com/common/oauth2/v2.0/token';
public $apiBaseUrl = 'https://login.microsoftonline.com/common/oauth2/v1.0';
public $scope = null;
public function init()
{
parent::init();
if ($this->scope === null)
{
$this->scope = 'https://graph.microsoft.com/User.Read';
}
}
/**
* Overrides default function to fix malformed url
*/
public function getReturnUrl()
{
return $this->returnUrl;
}
protected function defaultName()
{
return 'office365';
}
protected function defaultTitle()
{
return 'Office365';
}
/**
* For popup mode
*/
protected function defaultViewOptions()
{
return [
'popupWidth' => 800,
'popupHeight' => 500,
];
}
/**
* Gets new auth token to replace expired one.
*/
protected function initUserAttributes()
{
return $this->api('me', 'GET');
}
}
So, how can I authenticate through MS graph?
The yii\authclient package requires using the returnUrl having a request param authclient=live, e.g. https://example.com/site/auth?authclient=live
However, Azure prohibits request params in the returnUrl. Therefore, to make yii\authclient works with Azure, i.e., the returnUrl as https://example.com/site/auth/live. You need to prettify the url with request param as follows:
In config/main.php
'components' => [
'urlManager' => [
'class' => 'yii\web\UrlManager',
'enablePrettyUrl' => true,
'rules' => [
'site/auth/<authclient>' => 'site/auth'
]
]
]
In controllers/SiteController.php,
public function actions()
{
return [
'auth' => [
'class' => 'yii\authclient\AuthAction',
'successCallback' => [$this, 'onAuthSuccess']
]
];
}
...
public function onAuthSuccess($client) {
// get user data from client
$userAttributes = $client->getUserAttributes();
// DO YOUR THING
}

How to login Yii2 without database?

I need a help!
I have a working mechanism login with DB but sometimes i need get login process without DB (fake user use).
Static method in User model
public static function findByRoot()
{
$arr = [
'id' => 100,
'created_at' => 1444322024,
'updated_at' => 1444322024,
'username' => 'vasya',
'auth_key' => 'aagsdghfgukfyrtweri',
'password_hash' => 'aa2gsdg123hfgukfyrtweri',
'email' => 'some#email',
'status' => 10,
];
return new static($arr);
}
I too tried alternative variat method like:
public static function findByRoot()
{
$model = new User();
$model->id = '1000';
$model->username = 'vasya';
$model->status = 10;
return $model;
}
Yii::$app->getUser()->login() requires implements from UserIdentity
Do auth:
\Yii::$app->getUser()->login(User::findByRoot());
If I put real name from db in login method it returned TRUE and that's OK
But if put User::findByRoot() (the same object) it returned too TRUE but Yii::$app->user->identity has NULL
What's problem?
Yii::$app->user->identity returns null in case it can't find user's id. To fix that, first of all make sure, you supply the right id here:
public static function findIdentity($id)
{
// dump $id here somehow, does it belong to the static collection?
return isset(self::$users[$id]) ? new static(self::$users[$id]) : null;
}
Second option you have, is to always return the instance with filled data, since you use a fake data to test it anyway.
public static function findIdentity($id)
{
// just ignore the $id param here
return new static(array(
'updated_at' => '...',
'username' => '....',
// and the rest
));
}

zf2 How to get user details with AuthenticationService

I've created a module to authenticate a user. Now, after login I go to the index action and the system tells me that the authentication is all working fine. But What I want is to print some more user details from the Users table. When I try:
print_r($this->getServiceLocator()->get('AuthService')->getAdapter()->getResultRowObject());
I get no result. What am I doing wrong?
Thanks for your help.
In my module.php I've the following code(snippet):
public function getServiceConfig()
{
return array(
'abstract_factories' => array(),
'aliases' => array(),
'factories' => array(
// Some more code here but removed for simplicity
// Autentication
'AuthService' => function ($sm) {
$adapter = $sm->get('master_db');
$dbAuthAdapter = new DbAuthAdapter ( $adapter, 'Users', 'email', 'password' );
$auth = new AuthenticationService();
$auth->setAdapter ( $dbAuthAdapter );
return $auth;
},
// Some more code here but removed for simplicity
}
In my IndexController.php I've the following (snippets):
public function indexAction()
{
if(!$this->getServiceLocator()->get('AuthService')->hasIdentity()){
return $this->redirect()->toUrl('login');
}
echo "hello, it works!";
exit;
}
public function loginAction(){
$form = $this->getServiceLocator()->get('LoginForm');
$viewModel = new ViewModel(array('form' =>
$form));
return $viewModel;
}
public function processAction(){
// Lots of code here
if($bcrypt->verify($loginData['password'], $userData->password))
{
$this->getAuthService()
->getAdapter()
->setIdentity($loginData['email'])
->setCredential($userData->password);
$result = $this->getAuthService()->authenticate();
}
// Lots of code here where I check if $result->isValid and route to the
// correct action
}
public function getAuthService() {
if(!isset($this->authservice)) {
$this->authservice = $this->getServiceLocator()->get('AuthService');
}
return $this->authservice;
}
Instead of refering to the authentication result object (which properly only exists in the authentication request) you can simply store user details in the authentication identity (#see http://framework.zend.com/manual/2.1/en/modules/zend.authentication.intro.html).
For your case you could also store user specific details right after the validation of the authentication result in the authentication storage:
if ($result->isValid()) {
//authentication success
$resultRow = $this->authService->getAdapter()->getResultRowObject();
$this->authService->getStorage()->write(array(
'id' => $resultRow->id,
'user_agent' => $request->getServer('HTTP_USER_AGENT'))
);
}
(This information was taken from this authentication tutorial http://samsonasik.wordpress.com/2013/05/29/zend-framework-2-working-with-authenticationservice-and-db-session-save-handler/)

Categories