UserIdentity and session issue - yii

I have the following class. But when I try to access the Yii::app()->user->realName; it generates an error.
I can't understand it all. please help!
Following code is the code of my UserIdentity class.
<?php
/**
* UserIdentity represents the data needed to identity a user.
* It contains the authentication method that checks if the provided
* data can identity the user.
*/
class UserIdentity extends CUserIdentity {
public $id, $dmail, $real_name;
/**
* Authenticates a user.
* The example implementation makes sure if the username and password
* are both 'demo'.
* In practical applications, this should be changed to authenticate
* against some persistent user identity storage (e.g. database).
* #return boolean whether authentication succeeds.
*/
public function authenticate() {
$theUser = User::model()->findByAttributes(array(
'email' => $this->username,
// 'password' => $this->password
));
if ($theUser == null) {
$this->errorCode = self::ERROR_PASSWORD_INVALID;
} else {
$this->id = $theUser->id;
$this->setState('uid', $this->id);
// echo $users->name; exit;
// $this->setState('userName', $theUser->name);
$this->setState("realName",$theUser->fname .' '. $theUser->lname);
$this->errorCode = self::ERROR_NONE;
}
return!$this->errorCode;
}
}
?>

You need to extend the CWebUser class to achieve the results you want.
class WebUser extends CWebUser{
protected $_realName = 'wu_default';
public function getRealName(){
$realName = Yii::app()->user->getState('realName');
return (null!==$realName)?$realName:$this->_realName;
}
public function setRealName($value){
Yii::app()->user->setState('realName', $value);
}
}
You can then assign and recall the realName attribute by using Yii::app()->user->realName.
The protected $_realName is optional, but allows you to define a default value. If you choose not to use it, change the return line of the getRealName method to return $realName.
Place the above class in components/WebUser.php, or anywhere that it will be loaded or autoloaded.
Change your config file to use your new WebUser class and you should be all set.
'components'=>
'user'=>array(
'class'=>'WebUser',
),
...
),

Related

Symfony, PHPUnit : Client Webdriver Authentication

I need to authenticate my WebDriver Client for functional tests.
For example,
In my integration tests, i'm doing something like that :
namespace Tests\Controller;
use App\Entity\Donor;
use App\Entity\User;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\Tools\SchemaTool;
use SebastianBergmann\Type\RuntimeException;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
class DonorTest extends WebTestCase
{
private static $client;
/**
* #var EntityManager
*/
private $entityManager;
/**
* #var SchemaTool
*/
private $schemaTool;
public function __construct(?string $name = null, array $data = [], string $dataName = '')
{
parent::__construct($name, $data, $dataName);
static::ensureKernelShutdown();
if (!self::$client) {
self::$client = static::createClient([], [
'PHP_AUTH_USER' => 'Same Old User',
'PHP_AUTH_PW' => 'Same Old Password',
]);
}
$this->entityManager = self::bootKernel()
->getContainer()
->get('doctrine')
->getManager();
$this->schemaTool = new SchemaTool($this->entityManager);
/** Safeguard */
$connection = $this->entityManager->getConnection()->getParams();
if ($connection['driver'] != 'pdo_sqlite' || $connection['path'] != '/tmp/test_db.sqlite') {
throw new RuntimeException('Wrong database, darling ! Please set-up your testing database correctly. See /config/packages/test/doctrine.yaml and /tests/README.md');
}
}
I'm just passing the credentials in paramaters, and it works.
But, in my functional tests, i'm using the WebDriver. It didn't accept credentials in arguments :
<?php
namespace App\Tests\Functional\Entities\Donor;
use App\Entity\Donor;
use App\Tests\Functional\Helpers\Carrier\CarrierHelper;
use App\Tests\Functional\Helpers\Donor\DonorHelper;
use Doctrine\ORM\EntityManager;
use Facebook\WebDriver\WebDriverBy;
use Symfony\Component\Finder\Finder;
use Symfony\Component\Panther\PantherTestCase;
use Symfony\Component\Panther\Client;
class DonorTest extends PantherTestCase
{
/**
* #var EntityManager
*/
private $entityManager;
/**
* #var CarrierHelper
*/
private $helper;
/**
* #var Client
*/
private $client;
public function __construct(?string $name = null, array $data = [], string $dataName = '')
{
parent::__construct($name, $data, $dataName);
$this->entityManager = self::bootKernel()
->getContainer()
->get('doctrine')
->getManager();
$this->helper = new DonorHelper();
}
public static function setUpBeforeClass(): void
{
// Do something
}
public function setUp(): void
{
parent::setUp(); // TODO: Change the autogenerated stub
$this->client = Client::createChromeClient();
$this->client->manage()->window()->maximize();
}
I can't pass any login arguments in createChromeClient() method.
I think i have to play with cookies in cookieJar, or token, but i don't know how.
Feel free to ask me my ahtentication method, but i've followed the documentation :
https://symfony.com/doc/current/security/form_login_setup.html
EDIT
I've just tried something else. Log in with my browser, for generate a cookie, and tried to handcraft an other with same PHPSESSID
public function setUp(): void
{
parent::setUp(); // TODO: Change the autogenerated stub
$this->client = Client::createChromeClient();
$this->client->manage()->window()->maximize();
$cookie = new Cookie('PHPSESSID', 'pafvg5nommcooa60q14nqhool0');
$cookie->setDomain('127.0.0.1');
$cookie->setHttpOnly(true);
$cookie->setSecure(false);
$cookie->setPath('/');
$this->client->manage()->addCookie($cookie);
}
But get this error :
Facebook\WebDriver\Exception\InvalidCookieDomainException: invalid cookie domain
Domain is good, same as my web browser.
I will update as my investigations progressed.
EDIT 2
Ok... Got It.
According to this thread : Unable to set cookies in Selenium Webdriver
For setting-up cookie['domain'], you have to request firstly on the domain, THEN set-up the cookie...
SO, this is almost working :
public function setUp(): void
{
parent::setUp(); // TODO: Change the autogenerated stub
$this->client = Client::createChromeClient();
$this->client->manage()->window()->maximize();
$this->client->request('GET', 'http://127.0.0.1/randompage');
$handcookie = Cookie::createFromArray([
'name' => 'PHPSESSID',
'value' => 'pcvbf3sjlla16rfb1b1274qk01',
'domain' => '127.0.0.1',
'path' => '/'
]);
$this->client->manage()->addCookie($handcookie);
}
Next step : Find a way to generate a permanent cookie, without lifetime.
I think nobody will read this but i will update it in case someone else gets stuck.

Symfony 4 API Rest PUT : Map data to database entity

I'm a begginer in Symfony 4 and I'm developing an API Rest. I want to create a PUT resource that can handle many update cases.
In my case, I have a user with many properties but I will take an example with 3 properties to keep things simple :
User {
username,
email,
password
}
My PUT resource can be called in order to update Username, update Email or update Password. For example, to update Username, the user of my API will send a PUT request with only username :
{
username: "New username"
}
Same for email and password, he will only send the property he wants to change.
My problem is in my Controller, I have to do things like this :
/**
* #Rest\Put("/user/{id}")
* #param Request $request
* #return View
*/
public function putUserRequest(Request $request)
{
$userId = $request->get('id');
$user = $this->doctrine->getRepository(User::class)->findOneBy('id' => $userId);
$userFromRequest = $this->serializer->deserialize($request->getContent(), User::class, 'json');
if ($userFromRequest->getUsername() != NULL) {
$user->setUsername($userFromRequest->getUsername())
}
if ($userFromRequest->getEmail() != NULL) {
$user->setEmail($userFromRequest->getEmail())
}
if ($userFromRequest->getPassword() != NULL) {
$user->setPassword($userFromRequest->getPassword())
}
// ...
}
In my example I have only 3 properties, but imagine when I have 30 properties.
With Symfony 3 I used forms to validate / save my datas :
$form->submit($request->request->all(), $clearMissing);
Where $clearMissing is false to keep datas not provided by the user of my API. I can't find a way to do it with serializer but I guess I'm doing things wrong.
Thanks.
If I understand correctly, You can use the validator Component like this :
/**
* #Rest\Put("/user/{id}")
* #param Request $request
* #return View
*/
public function putUserRequest(User $user, Request $request, ValidatorInterface $validator)
{
$data = $request->getContent();
$this->serializer->deserialize($data, User::class, 'json', ['object_to_populate' => $user]);
//At this moment, the data you sent is merged with your user entity
/** #var ConstraintViolationList $errors */
$errors = $validator->validate($user, null ,['groups' => 'user_update']);
if (count($errors) > 0) {
//return json reponse with formated errors;
}
//if ok $entityManager->flush() and Response Json with serialization group;
...
}
In your user class :
class User implements UserInterface
{
/**
* #Assert\Email(groups={"user_create", "user_update"})
*/
private $email;
/**
* #Assert\NotBlank(groups={"user_create", "user_update"})
* #Assert\Length(min=7, groups={"user_create", "user_update"})
*/
private $password;
/**
* #Assert\Length(min=2, groups={"user_create", "user_update"} )
*/
private $username;
}
Related Validator component documentation : https://symfony.com/doc/current/validation/groups.html
You can also check this project : https://github.com/attineos/relation-deserializer

Cartalyst Sentry a login is required, none given

I use Cartalyst Sentry for authentication. When I try to seed my database from command line, I get this error:
[Cartalyst\Sentry\Users\LoginRequiredException]
A login is required for a user, none given.
This is my DatabaseSeeder.php file:
<?php
class DatabaseSeeder extends Seeder {
/**
* Run the database seeds.
*
* #return void
*/
public function run()
{
Eloquent::unguard();
$this->call('PriceChangeOccuredTableSeeder');
}
}
And this is my PriceChangeOccuredTableSeeder.php file:
<?php
// Composer: "fzaninotto/faker": "v1.3.0"
use Faker\Factory as Faker;
class PriceChangeOccuredTableSeeder extends Seeder {
public function run() {
DB::table('price_change_occured')->delete();
User::create(array('product_id' => 1, 'owner_id' => 2, 'change_occured' => 1));
}
}
And this is my PriceChangeOccured model
<?php
class PriceChangeOccured extends \Eloquent {
public $table = 'price_change_occured';
}
if change your login from "EMAIL" to "USERNAME" ,and every thing was good,you should change these too:
in your seed add username too:
class UserSeeder extends Seeder
{
protected $admin_email = "admin#admin.com";
protected $admin_password = "password";
protected $admin_username = "naghipour.me";
}
find SentryUserRepository.php and find create function and change to this:
$data = array(
"email" => $input["email"],
"password" => $input["password"],
"username" => $input["username"]
);
Attention: you should add username filed in migration :
$table->string('username', 100)->unique();
and every where there is email you should add username too.
The error says that you are not adding a login to the user. And it seems that you aren't. Thus, you only need to add the login (or email) depending on how your User table works.
I'm assuming that a email/login and password woud be required for your user table. And you are not providing any of those when you create the user on this example.

how to get or create role in yii

I am a newbie to yii. I have stuck my mind with yii-tutorials for creating roles in yii. but I am not getting how can I create role in yii. means I want to create two roles admin & staff and I want to give different priviliage to them.
I have created a role in user table, but I am not getting that how can I create a role and can assign priviliages to them, please help me guys
Thanks in advance
In your copenents/UserIdentity.php
class UserIdentity extends CUserIdentity{
private $_id;
public function authenticate()
{
$record=Members::model()->findByAttributes(array('username'=>trim($this->username)));
if($record===null)
$this->errorCode=self::ERROR_USERNAME_INVALID;
else if($record->password!==md5(trim($this->password)))
$this->errorCode=self::ERROR_PASSWORD_INVALID;
else
{
$this->_id=$record->id;
$this->setState('username', $record->username);
$this->setState('name', $record->name);
$this->setState('type', $record->role);
$this->errorCode=self::ERROR_NONE;
}
return !$this->errorCode;
}
public function getId()
{
return $this->_id;
}
public function setId($id)
{
$this->_id = $id;
}
}
You can create a new column name as "role". set the members type "admin" or "staff" to role column.
Be careful to that line.
$this->setState('type', $record->role);
Create a new helper file. /protected/helpers/RoleHelper.php
class RoleHelper {
public static function GetRole(){
if (Yii::app()->user->type == "admin"){
//set the actions which admin can access
$actionlist = "'index','view','create','update','admin','delete'";
}
elseif (Yii::app()->user->type = "staff"){
//set the actions which staff can access
$actionlist = "'index','view','create','update'";
}
else {
$actionlist = "'index','view'";
}
return $actionlist;
}
}
and in your controllers -> accessRules function
public function accessRules()
{
return array(
array('allow', // allow authenticated user to perform 'create' and 'update' actions
'actions'=>array(RoleHelper::GetRole()),
'users'=>array('#'),
),
array('deny', // deny all users
'users'=>array('*'),
),
);
}
and dont forget to add 'application.helpers.*' to /config/main.php
'import'=>array(
'application.models.*',
'application.components.*',
'application.helpers.*',
),
This source is pretty good specially for beginners..I am using this method till now:
Simple RBAC in YII
Just follow the instructions given while having your desired modifications.
Concrete Example:
WebUser.php (/components/WebUser.php)
<?php
class WebUser extends CWebUser
{
/**
* Overrides a Yii method that is used for roles in controllers (accessRules).
*
* #param string $operation Name of the operation required (here, a role).
* #param mixed $params (opt) Parameters for this operation, usually the object to access.
* #return bool Permission granted?
*/
public function checkAccess($operation, $params=array())
{
if (empty($this->id)) {
// Not identified => no rights
return false;
}
$role = $this->getState("evalRoles");
if ($role === 'SuperAdmin') {
return 'SuperAdmin'; // admin role has access to everything
}
if ($role === 'Administrator') {
return 'Administrator'; // admin role has access to everything
}
if ($role === 'Professor') {
return 'Professor'; //Regular Teaching Professor, has limited access
}
// allow access if the operation request is the current user's role
return ($operation === $role);
}
}
Just connect it with your components/UserIdentity.php and config/main.php:
'components' => array(
// ...
'user' => array(
'class' => 'WebUser',
),
and thats it..
to check the role of the logged in:
Yii::app->checkAccess("roles");
where checkAccess is the name of your function in WebUser...
Please note that evalRoles is a column in your table that will supply the role of an account (on my link provided, that would be the word roles used in the major part snippet)

Save user's last log out

I am trying to save user's last logout time into a DB in Yii framework.
I have WebUser as:
<?php
// this file must be stored in:
// protected/components/WebUser.php
class WebUser extends CWebUser {
public function afterLogout()
{
$user=user::Model();
$user->logOutDateTime='TEST';
$user->saveAttributes(array('logOutDateTime'));
parent::afterLogout();
}
}
?>
and in config\main.php I have these lines
// application components
'components'=>array(
'user'=>array(
// enable cookie-based authentication
'class'=>'WebUser',
'allowAutoLogin'=>true,
)
For now I have set logOutDateTime datatype to varchar, to test, and I assume every time user logs out, it should write 'TEST' into database but it does nothing.
Where did I go wrong?
I don't think the afterLogout() still has the Yii::app()->user set, so I would do something like (untested):
public function beforeLogout()
{
if (parent::beforeLogout()) {
$user = User::model()->findByPk(Yii::app()->user->id); // assuming you have getId() mapped to id column
$user->logOutDateTime='TEST';
$user->saveAttributes(array('logOutDateTime'));
return true;
} else {
return false;
}
}
$user = user::Model();
should be:
$user = user::Model()->find(/* model_conditions */);