Preventing login from multiple device in yii2 framework - security

I am trying to prevent the login from another device. And after all the research i have found using session. I am using the default login system of yii2 framework. And added these codes in the user model.
The user model is:
<?php
namespace app\models;
use Yii;
//app\models\Users is the model generated using Gii from users table
use app\models\Users as DbUser;
class User extends \yii\base\Object implements \yii\web\IdentityInterface {
public $id;
public $username;
public $password;
public $authKey;
public $token;
public $email;
public $any;
public $user_type;
/**
* #inheritdoc
*/
public static function findIdentity($id) {
$dbUser = DbUser::find()
->where([
"id" => $id
])
->one();
if (!count($dbUser)) {
return null;
}
return new static($dbUser);
}
/**
* #inheritdoc
*/
public static function findIdentityByAccessToken($token, $userType = null) {
$dbUser = DbUser::find()
->where(["token" => $token])
->one();
if (!count($dbUser)) {
return null;
}
return new static($dbUser);
}
/**
* Finds user by username
*
* #param string $username
* #return static|null
*/
public static function findByUsername($username) {
$dbUser = DbUser::find()
->where([
"username" => $username
])
->one();
if (!count($dbUser))
{
return null;
}
return new static($dbUser);
}
/**
* #inheritdoc
*/
public function getId() {
return $this->id;
}
/**
* #inheritdoc
*/
public function getAuthKey()
{
return $this->authKey;
}
/**
* #inheritdoc
*/
public function validateAuthKey($authKey)
{
return $this->authKey === $authKey;
}
/**
* Validates password
*
* #param string $password password to validate
* #return boolean if password provided is valid for current user
*/
public function validatePassword($password)
{
return Yii::$app->getSecurity()->validatePassword($password, $this->password);
}
public function session_validate()
{
// Encrypt information about this session
$user_agent = $this->session_hash_string($_SERVER['HTTP_USER_AGENT'], $this->any);
// Check for instance of session
if ( session_exists() == false )
{
// The session does not exist, create it
$this->session_reset($user_agent);
}
// Match the hashed key in session against the new hashed string
if ( $this->session_match($user_agent) )
{
return true;
}
// The hashed string is different, reset session
$this->session_reset($user_agent);
return false;
}
/**
* session_exists()
* Will check if the needed session keys exists.
*
* #return {boolean} True if keys exists, else false
*/
private function session_exists()
{
return isset($_SESSION['USER_AGENT_KEY']) && isset($_SESSION['INIT']);
}
/**
* session_match()
* Compares the session secret with the current generated secret.
*
* #param {String} $user_agent The encrypted key
*/
private function session_match( $user_agent )
{
// Validate the agent and initiated
return $_SESSION['USER_AGENT_KEY'] == $user_agent && $_SESSION['INIT'] == true;
}
/**
* session_encrypt()
* Generates a unique encrypted string
*
* #param {String} $user_agent The http_user_agent constant
* #param {String} $unique_string Something unique for the user (email, etc)
*/
private function session_hash_string( $user_agent, $unique_string )
{
return md5($user_agent.$unique_string);
}
/**
* session_reset()
* Will regenerate the session_id (the local file) and build a new
* secret for the user.
*
* #param {String} $user_agent
*/
private function session_reset( $user_agent )
{
// Create new id
session_regenerate_id(TRUE);
$_SESSION = array();
$_SESSION['INIT'] = true;
// Set hashed http user agent
$_SESSION['USER_AGENT_KEY'] = $user_agent;
}
/**
* Destroys the session
*/
private function session_destroy()
{
// Destroy session
session_destroy();
}
}

this a better way of doing it is below.
you do is that you add an extra column to your users table, name it 'conc_login' or preferably text because it might be difficult to predict the size of data we are expecting.
when the user logs in, check if the logins column is empty, if it is empty, create a json that contains the session_id, time of login using the time() function.
If the logins column is not empty or the count of the column when decoded is greater than zero, then you check if the count is greater than login limit , if the number of logins is not yet greater than the login limit, then append the new session to the logins column in the database table
If the login limit is reached then check through the logins and check for the one with expired time, for example, a user that is not active of 300secnds is assumed to be logged out, then you delete the session that has expired from the table
In any request that is been made by a logged user you check if the session key still exists in the login column of the database ($logins[‘session_key’]), and if it is not found then log the user out immediately to avoid escalation of right and if otherwise then update the $login[‘time’] to the new time().
You can implement this code.
Login Form Model Add this function for concurrent user validation
$get_limit = Setting::findOne(['name' => 'login_limit']);
$login_limit = 3; //$get_limit->value;
$active_sess = User::findOne($getUser->id);
if ($active_sess->conc_login == '' or count(json_decode($active_sess->conc_login)) == 0) {
$login_json = json_encode([
[Yii::$app->session->getId() => Yii::$app->session->getId(), 'session_key' => Yii::$app->session->getId(), 'time' => time()]
]);
$active_sess->conc_login = $login_json;
$active_sess->save();
} else if (count(json_decode($active_sess->conc_login)) > 0 and count(json_decode($active_sess->conc_login)) $login_json = json_decode($active_sess->conc_login);
$login_json[] = [Yii::$app->session->getId() => Yii::$app->session->getId(), 'session_key' => Yii::$app->session->getId(), 'time' => time()];
$login_json = json_encode($login_json);
//print_r($login_json); exit;
$active_sess->conc_login = $login_json;
$active_sess->save();
} else if (count(json_decode($active_sess->conc_login)) >= $login_limit) {
$logins = json_decode($active_sess->conc_login);
foreach ($logins as $key => $login) {
if ($login->time < time() - 120) {
//this checks if the iterated login is greater than the current time -120seconds and if found to be true then the user is inactive
//then set this current login to null by using the below statement
//$logins[$key] = null; // or unset($logins[$key]) either should work;
unset($logins[$key]);
}
}
//after iteration we check if the count of logins is still greater than the limit
if (count($logins) >= $login_limit) {
//then return a login error that maximum logins reached
//echo 'you are not allowed to login as you have breeched the maximum session limit.';
//exit;
$login_json = json_encode($logins);
$active_sess->conc_login = $login_json;
$active_sess->save();
$this->addError($attribute, 'you are not allowed to login as you have breeched the maximum session limit.');
} else {
//then login is successsfull
$login_json = [];
foreach ($logins as $key => $val) {
$login_json[] = [$val->session_key => $val->session_key, 'session_key' => $val->session_key, 'time' => $val->time];
}
$login_json[] = [Yii::$app->session->getId() => Yii::$app->session->getId(), 'session_key' => Yii::$app->session->getId(), 'time' => time()];
$login_json = json_encode($login_json);
$active_sess->conc_login = $login_json;
$active_sess->save();
}
//update the logins column to equal to json_encode($logins);
}
User side update session every 60seconds : SiteController
public function actionUserSessionUpdate() {
$session = Yii::$app->session;
$userid = $session->get('userid');
$username = $session->get('username');
$data = array('session_id' => Yii::$app->session->getId());
$isUserLogin = (!empty($userid) && !empty($username)) ? 'true' : 'false';
if ($isUserLogin == 'false') {
echo 'gotologin'; exit;
//return $this->redirect(['/login']);
} else {
//Login user
$active_sess = Clientmanagement::findOne($userid);
$loginjson = json_decode($active_sess->conc_login);
$login_json = [];
foreach ($loginjson as $key => $val) {
if ($val->session_key == Yii::$app->session->getId()) {
$login_json[] = [$val->session_key => $val->session_key, 'session_key' => $val->session_key, 'time' => time()];
} else {
$login_json[] = [$val->session_key => $val->session_key, 'session_key' => $val->session_key, 'time' => $val->time];
}
}
$login_json = json_encode($login_json);
$active_sess->conc_login = $login_json;
$active_sess->save();
}
exit;
}
Client ajax
<?php
if (!empty($session->get('userid'))) {
$usersession_url = Yii::$app->urlManager->createAbsoluteUrl(["/site/user-session-update"]);
$scriptb = <<< JS
$(document).ready(function () {
//Wait 1 Minutes
setInterval(function(){
$.ajax({
type:"post",
data:'id=session',
url:"$usersession_url",
success:function(data){
}
});
}, 60000);
});
JS;
$this->registerJs($scriptb);
}
?>
UserController Logout
public function actionLogout() {
$session = Yii::$app->session;
$userid = $session->get('userid');
//concurrent active user session removed
$active_sess = Clientmanagement::findOne($userid);
$loginjson = json_decode($active_sess->conc_login);
foreach ($loginjson as $key => $login) {
if ($login->session_key == Yii::$app->session->getId()) {
unset($loginjson[$key]);
}
}
$login_json = json_encode($loginjson);
$active_sess->conc_login = $login_json;
$active_sess->save();
$session->destroy();
return $this->redirect(['/login']);
}

We can set that code in component and it will also work for every controller and action. Also, I changed a code
common/components/LoginDevicesLimit.php
<?php
namespace app\common\components;
use Yii;
/**
* Logged In Devices Storage Helper
*/
class LoginDevicesLimit extends \yii\base\Component
{
public function init() {
$this->saveLoginSession();
parent::init();
}
public function saveLoginSession(){
$login_limit = env('DEVICES_LIMIT', 3);
$active_sess = \Yii::$app->user->identityClass::findOne(\Yii::$app->user->id);
if(!$active_sess){
return false;
}
$session_count = count(json_decode($active_sess->conc_login, true));
if ($active_sess->conc_login == '' || $session_count == 0) {
$login_json = json_encode([
Yii::$app->session->getId() => ['session_key' => Yii::$app->session->getId(), 'time' => time()]
]);
$active_sess->conc_login = $login_json;
$active_sess->save();
} else if ($session_count > 0 && $session_count < $login_limit) {
$login_json = json_decode($active_sess->conc_login, true);
$login_json[Yii::$app->session->getId()] = ['session_key' => Yii::$app->session->getId(), 'time' => time()];
$login_json = json_encode($login_json);
//print_r($login_json); exit;
$active_sess->conc_login = $login_json;
$active_sess->save();
} else if ($session_count > $login_limit) {
$logins = json_decode($active_sess->conc_login, true);
foreach ($logins as $key => $login) {
if ($login->time < time() - 120) {
//this checks if the iterated login is greater than the current time -120seconds and if found to be true then the user is inactive
//then set this current login to null by using the below statement
//$logins[$key] = null; // or unset($logins[$key]) either should work;
unset($logins[$key]);
}
}
//after iteration we check if the count of logins is still greater than the limit
if (count($logins) > $login_limit) {
//then return a login error that maximum logins reached
//echo 'you are not allowed to login as you have breeched the maximum session limit.';
//exit;
$login_json = json_encode($logins);
$active_sess->conc_login = $login_json;
$active_sess->save();
throw new \yii\web\HttpException(404, 'You are not allowed to login as you have reached the maximum session limit.');
} else {
//then login is successsfull
$login_json = [];
foreach ($logins as $key => $val) {
$login_json[] = [$val->session_key => $val->session_key, 'session_key' => $val->session_key, 'time' => $val->time];
}
$login_json[] = [Yii::$app->session->getId() => Yii::$app->session->getId(), 'session_key' => Yii::$app->session->getId(), 'time' => time()];
$login_json = json_encode($login_json);
$active_sess->conc_login = $login_json;
$active_sess->save();
}
}
}
}

You can generate additional authentication code during login process that will be stored in user's identity and saved in db as well. Every time user invokes action his code is compared to this saved in db and if doesn't match user is forcefully logged out . This way only the latest login is valid. refer this link

This is an alternative way to prevent the login from another device.
You need to configure your application as shown below using the webvimark/user-management library.
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(255) NOT NULL,
`auth_key` varchar(32) NOT NULL,
`password_hash` varchar(255) NOT NULL,
`confirmation_token` varchar(255) DEFAULT NULL,
`status` int(11) NOT NULL DEFAULT '1',
`superadmin` smallint(1) DEFAULT '0',
`created_at` int(11) NOT NULL,
`updated_at` int(11) NOT NULL,
`registration_ip` varchar(15) DEFAULT NULL,
`bind_to_ip` varchar(255) DEFAULT NULL,
`email` varchar(128) DEFAULT NULL,
`email_confirmed` smallint(1) NOT NULL DEFAULT '0',
`sessions` longtext,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8
<?php
$config =
[
'bootstrap' => ['log', 'LogoutDevices'],
'components' =>
[
'user' => ['class' => 'webvimark\modules\UserManagement\components\UserConfig', 'enableAutoLogin' => true, 'on afterLogin' => function($event) {(new \app\components\ABCDEFG\DevicesLimit\LoginDevicesLimit())->sessionManager(); \webvimark\modules\UserManagement\models\UserVisitLog::newVisitor($event->identity->id);}],
'LogoutDevices' => ['class' => 'app\components\ABCDEFG\DevicesLimit\LogoutDevices']
]
]
Then, you need to use the classes below:
<?php
namespace app\models\UserManagement;
use Yii;
/**
* This is the model class for table "user".
*
* #property string $sessions
*/
class User extends \webvimark\modules\UserManagement\models\User
{
function __construct($config = [])
{
parent::__construct($config);
}
function init()
{
parent::init();
}
}
<?php
namespace app\components\ABCDEFG\DevicesLimit;
use Yii;
use yii\base\Component;
use app\models\UserManagement\User;
/**
* Logged In Devices Storage Helper
*
* Class LoginDevicesLimit
* #package app\components\ABCDEFG\DevicesLimit
*
* #property integer $iDevicesLimit
*
*/
class LoginDevicesLimit extends Component
{
private int $iDevicesLimit = 1;
public function init()
{
parent::init();
}
public function getDevicesLimit() : int
{
return $this->iDevicesLimit;
}
public function setDevicesLimit(int $iDevicesLimit = 1) : self
{
if($iDevicesLimit >= 1)
{
$this->iDevicesLimit = $iDevicesLimit;
}
return $this;
}
public function sessionManager() : self
{
if(isset(Yii::$app->user->id))
{
$oUser = User::findOne(Yii::$app->user->id);
if(!empty($oUser))
{
if(User::hasRole('Expert', $superAdminAllowed = false))
{
$this->setDevicesLimit(3);
}
$sSessionsJSON = $oUser->sessions;
$aSessions = json_decode($sSessionsJSON, true);
$aSession = Helper::getCurrentSessionData();
if(is_array($aSessions) && !empty($aSessions))
{
$bIsSessionExists = false;
foreach($aSessions as $iSessionKey => $aSessionData)
{
if($aSessionData['id'] == $aSession['id'])
{
$aSessions[$iSessionKey] = $aSession;
$bIsSessionExists = true;
break;
}
}
if($bIsSessionExists == true)
{
$aTime = array_column($aSessions, 'time');
array_multisort
(
$aTime, SORT_NUMERIC, SORT_ASC,
$aSessions
);
}
else
{
array_unshift($aSessions, $aSession);
}
}
else
{
$aSessions[] = $aSession;
}
$aSessions = array_slice($aSessions, 0, $this->getDevicesLimit());
$sSessionsJSON = json_encode($aSessions);
if(json_last_error() == JSON_ERROR_NONE)
{
$oUser->sessions = $sSessionsJSON;
$oUser->save();
}
}
}
return $this;
}
}
<?php
namespace app\components\ABCDEFG\DevicesLimit;
use Yii;
use yii\base\Component;
use app\models\UserManagement\User;
/**
* Logged In Devices Storage Helper
*
* Class LogoutDevices
* #package app\components\ABCDEFG\DevicesLimit
*
*/
class LogoutDevices extends Component
{
public function init()
{
parent::init();
self::logoutOnExceedingLimit();
}
public static function logoutOnExceedingLimit()
{
$xRetValue = NULL;
if(Yii::$app->request->isAjax == false && isset(Yii::$app->user->id))
{
$oUser = User::findOne(Yii::$app->user->id);
if(!empty($oUser))
{
$sSessionsJSON = $oUser->sessions;
$aSessions = json_decode($sSessionsJSON, true);
$aSession = Helper::getCurrentSessionData();
if(is_array($aSessions) && !empty($aSessions))
{
$bIsSessionExists = in_array($aSession['id'], array_column($aSessions, 'id'));
if($bIsSessionExists == false)
{
Yii::$app->session->setFlash('devices-limit', true);
Yii::$app->session->close();
Yii::$app->user->logout(false);
$xRetValue = Yii::$app->response->redirect(['site/devices-limit-reached'], 302);
}
}
}
}
return $xRetValue;
}
}
<?php
namespace app\components\ABCDEFG\DevicesLimit;
use Yii;
use yii\base\Component;
/**
* Helper
*
* Class Helper
* #package app\components\ABCDEFG\DevicesLimit
*
*/
class Helper extends Component
{
public function init()
{
parent::init();
}
public static function getCurrentSessionData() : array
{
return
[
'id' => Yii::$app->session->getId(),
'time' => time(),
'device' => Yii::$app->request->userAgent,
];
}
}

Related

ES5 how to implement .Net IEqualityComaparer on data class for comparisons?

When I want to use a custom type T as hash key in .Net, I implement IEqualityComparer and pass it to hash map like Dictionary or HashSet, when adding new item, the GetHashCode and Equals method will be called to check whether two T instance are same.
for example, I have a immutable data class Foo:
sealed class Foo
{
public Foo(int field1, string field2)
{
Prop_1 = field1;
Prop_2 = field2;
}
public int Prop_1 { get; }
public string Prop_2 { get; }
}
and FooEuqalityComparer:
sealed class FooEuqalityComparer : IEqualityComparer<Foo>
{
public bool Equals(Foo x, Foo y)
{
return x == null ? y == null :
x.Prop_1 == y.Prop_1 &&
x.Prop_2 == y.Prop_2;
}
public int GetHashCode(Foo obj)
{
if (obj == null)
return 0;
return obj.Prop_1.GetHashCode() ^ obj.Prop_2.GetHashCode();
}
}
test:
var set = new HashSet<Foo>(new FooEuqalityComparer());
var foo1 = new Foo(1, "foo 1");
var not_foo2 = new Foo(1, "foo 1");
var foo3 = new Foo(3, "foo 3");
set.Add(foo1);
set.Add(not_foo2);
Assert.AreEqual(1, set.Count);
Assert.AreSame(foo1, set.Single());
set.Add(foo3);
Assert.AreEqual(2, set.Count);
How can I do it in nodejs?
Overwrite toString() is not a option because I want to keep reference to that object as key inside map.
After some search, I realized that javascript or ECMAScript use SameValueZero algorithm to compare objects, the best way still is using string as key.
so I use two map to achieve this:
class ObjectKeyMap {
/**
* #param {Object[]} keys -
* #param {function():string} keys[].getHashCode -
* #param {function(Object):T} valueSelector -
*
* #typedef {Object} T
*/
constructor(keys, valueSelector) {
const keyReferences = {};
keys.forEach(it => {
keyReferences[it.getHashCode()] = it;
});
this.keyReferences = keyReferences;
this.map = new Map(keys.map(it => [it.getHashCode(), valueSelector(it)]));
}
/**
* #param {string|{getHashCode:function():string}} key -
*
* #returns {string}
*/
_getStringKey(key) {
if (!key) {
return null;
}
if (Object.prototype.toString.call(key) === "[object String]") {
return key;
} else {
return key.getHashCode();
}
}
/**
* #param {string|{getHashCode:function():string}} key -
*
* #returns {T}
*/
get(key) {
const stringKey = this._getStringKey(key);
if (!stringKey || stringKey === "") {
return null;
}
return this.map.get(stringKey);
}
values() {
return [...this.map.values()];
}
/**
* #param {string|{getHashCode:function():string}} key -
*/
key(key) {
const stringKey = this._getStringKey(key);
if (!stringKey || stringKey === "") {
return null;
}
return this.keyReferences[stringKey];
}
keys() {
return Object.values(this.keyReferences).slice();
}
}
ObjectKeyMap assumes object to be used as key must have a getHashCode function which return identity string. It should be more readable if written in TypeScript.

custom validate in shopware shipping address in confirm order page

How to add my custom validate in shopware shipping address in confirm order page .once if success that process to confirm order. Please explain how to do this.
Can't you just add additional field to address while address creation?
public static function getSubscribedEvents()
{
return array(
'Shopware_Form_Builder' => ['onFormBuild', 1000]
);
}
public function onFormBuild(\Enlight_Event_EventArgs $event)
{
if (
( $event->getReference() !== \Shopware\Bundle\AccountBundle\Form\Account\PersonalFormType::class &&
$event->getReference() !== \Shopware\Bundle\AccountBundle\Form\Account\AddressFormType::class )
) {
return;
}
/** #var \Symfony\Component\Form\Form $builder */
$builder = $event->getBuilder();
$builder->get('additional')
->add('yourFieldName', \Symfony\Component\Form\Extension\Core\Type\TextType::class, [
'constraints' => [
new NotBlank()
]
]);
}
If no, then you should subscribe to checkout postdispatch and check what you want:
public static function getSubscribedEvents()
{
return array(
'Enlight_Controller_Action_PreDispatch_Frontend_Checkout' => 'onFrontendPreDispatchCheckout',
);
}
/**
* #param \Enlight_Controller_ActionEventArgs $args
*/
public function onFrontendPreDispatchCheckout(\Enlight_Controller_ActionEventArgs $args)
{
/**#var $subject \Shopware_Controllers_Frontend_Checkout */
$subject = $args->getSubject();
$request = $subject->Request();
$response = $subject->Response();
$action = $request->getActionName();
$view = $subject->View();
if (!$request->isDispatched() || $response->isException() ||
// only in case action is payment or confirm we should chcek it
($action != 'payment' && $action != 'confirm') ||
!$view->hasTemplate()
) {
return;
}
$validation = $this->thereWeCheckIt();
if (
$validation['message']
) {
$subject->forward('index', 'account', 'frontend', array(
'errorVariable' => $validation['message'],
));
}
}
Then you need also postDispatch account controller and show errorVariable to customer.

Joomla 2.5 component : Invalid controller: name='', format='raw'

im developing a custom joomla 2.5 component integrating jqGrid.
im setting up a controller task to handle (update mysql record) data sent from jqGrid postData
var grid = jQuery(\"#list\");
grid.jqGrid({
onSelectRow: function(id){
$('#list').editRow(id, true);
},
url: '/index.php?option=com_nutraidev&view=products&format=raw',
datatype: 'json',
mtype: 'GET',
colNames: [...],
rowNum: 25,
rowList: [5, 10, 20, 50],
height: 'auto',
pager: '#pager',
loadonce: true,
sortname: 'ID',
viewrecords: true,
direction:'RTL',
autowidth: true,
sortorder: \"desc\",
caption: 'abc',
width: '70%',
editurl:'/index.php?option=com_nutraidev&view=products&task=Products.save&token=". JUtility::getToken() ."=1',
postData: {'code':\" \",....},
.....
this is the url which makes the request
/index.php?option=com_nutraidev&view=products&task=Products.save&token=". JUtility::getToken() ."=1&format=raw
im getting:
error 500
Invalid controller: name='products', format='raw'
tried it from the browser with the actual token - same result.
here is the important part of my code:
com_nutraidev
- controllers
- products.php
require_once JPATH_COMPONENT.'/controller.php';
class NutraidevControllerProducts extends NutraidevController
{
public function &getModel($name = 'Products', $prefix = 'NutraidevModel')
{
$model = parent::getModel($name, $prefix, array('ignore_request' => true));
return $model;
}
public function save()
{
// Check for request forgeries.
JSession::checkToken() or jexit(JText::_('JINVALID_TOKEN'));
// Initialise variables.
$app = JFactory::getApplication();
$model = $this->getModel('Products', 'NutraidevModel');
$data = JRequest::get('get');
// Attempt to save the data.
$return = $model->updItem($data);
}
}
com_nutraidev
- models
- products.php
jimport('joomla.application.component.modellist');
class NutraidevModelProducts extends JModelList {
public function __construct($config = array()) {
parent::__construct($config);
}
public function updItem($data)
{
// set the variables from the passed data
$code = $data['code'];
$name = $data['name'];
// set the data into a query to update the record
$db = $this->getDbo();
$query = $db->getQuery(true);
$query->clear();
$query->update(' #__product ');
$query->set(' name = '.$db->Quote($name) );
$query->where(' code = ' . (int) $code );
$db->setQuery((string)$query);
if (!$db->query()) {
JError::raiseError(500, $db->getErrorMsg());
return false;
} else {
return true;
}
}
}
com_nutraidev
- views
- products
- view.raw.php
jimport('joomla.application.component.view');
class NutraidevViewProducts extends JView
{
/**
* Display the view
*/
public function display($tpl = null)
{
$app = JFactory::getApplication();
$document = JFactory::getDocument();
// Get data from the model
$items = $this->get('Items');
$this->state = $this->get('State');
$this->params = $app->getParams('com_nutraidev');
// Check for errors.
if (count($errors = $this->get('Errors'))) {;
throw new Exception(implode("\n", $errors));
}
// Assign data to the view
$response->page = 1;//JRequest::getVar('page');
$response->total = 1;//JRequest::getVar('total');
$response->records = count($items);
$i=0;
for ($i = 0; $i < count($items); ++$i) {
$response->rows[$i]['id'] = intval($items[$i]->code); //id
$response->rows[$i]['cell'] = array($items[$i]->code,
$items[$i]->name
);
}
echo json_encode($response);
jexit();
}
public function save($tpl = null)
{
echo "test";
jexit();
}
}
looking at other questions that had the similar issue i double checked my
administrator/components/nutraidev/nutraidev.xml
and made sure
<files folder="site">
<filename>controller.php</filename>
was there.
what could be the reason im getting this error ? i tried it also with view.json.php
and got the same result. thanks

symfony2 can't login “Bad credentials”

I am writing a website using the Symfony framework but for some reason, the login process is not working.
I always get message: Bad credentials
Here is my security.yml
# app/config/security.yml
jms_security_extra:
secure_all_services: false
expressions: true
security:
firewalls:
secured_area:
pattern: ^/
anonymous: ~
form_login:
login_path: /login
check_path: /login_check
access_control:
- { path: ^/admin, roles: ROLE_ADMIN }
providers:
in_memory:
memory:
users:
user: { password: userpass, roles: [ 'ROLE_USER' ] }
contant_manager: { password: manpass, roles: [ 'ROLE_CONTENT_MANAGER' ] }
admin: { password: adminpass, roles: [ 'ROLE_ADMIN' ] }
encoders:
Symfony\Component\Security\Core\User\User: plaintext
My User.php class
<?php
namespace YouMustKnowIt\NewsBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\AdvancedUserInterface;
use Doctrine\Common\Collections\ArrayCollection;
use APY\DataGridBundle\Grid\Mapping as GRID;
/**
* #ORM\Table(name="users")
* #ORM\Entity(repositoryClass="\YouMustKnowIt\NewsBundle\Entity\UserRepository")
*
* #GRID\Source(columns="id, username, email, role.name, isActive")
*/
class User implements AdvancedUserInterface, \Serializable
{
/**
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*
* #GRID\Column(filterable=false)
*/
private $id;
/**
* #ORM\Column(type="string", length=25, unique=true)
*/
private $username;
/**
* #ORM\Column(type="string", length=32)
*/
private $salt;
/**
* #ORM\Column(type="string", length=100)
*/
private $password;
/**
* #ORM\Column(type="string", length=60, unique=true)
*/
private $email;
/**
* #ORM\ManyToMany(targetEntity="RolesList", inversedBy="users")
*
* #GRID\Column(field="roleslist.role", type="text", filter="select", title="role")
*/
private $role;
/**
* #ORM\OneToMany(targetEntity="NewsCatalog", mappedBy="user")
* #ORM\Column(name="created_news", nullable=true)
*/
private $createdNews;
/**
* #ORM\Column(name="is_active", type="boolean")
*/
private $isActive;
public function __construct()
{
$this->isActive = false;
$this->salt = md5(uniqid(null, true));
$this->role = new \Doctrine\Common\Collections\ArrayCollection();
$this->createdNews = new \Doctrine\Common\Collections\ArrayCollection();
}
public function __toString()
{
return $this->username;
}
public function isAccountNonExpired()
{
return true;
}
public function isAccountNonLocked()
{
return true;
}
public function isCredentialsNonExpired()
{
return true;
}
public function isEnabled()
{
return $this->isActive;
}
/**
* #inheritDoc
*/
public function getRoles()
{
return $this->role->toArray();
}
/**
* #inheritDoc
*/
public function getUsername()
{
return $this->username;
}
/**
* #inheritDoc
*/
public function getSalt()
{
return $this->salt;
}
/**
* #inheritDoc
*/
public function getPassword()
{
return $this->password;
}
/**
* #inheritDoc
*/
public function getEmail()
{
return $this->email;
}
/**
* #inheritDoc
*/
public function eraseCredentials()
{
}
/**
* #see \Serializable::serialize()
*/
public function serialize()
{
return serialize(array(
$this->id,
));
}
/**
* #see \Serializable::unserialize()
*/
public function unserialize($serialized)
{
list (
$this->id,
) = unserialize($serialized);
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set username
*
* #param string $username
* #return User
*/
public function setUsername($username)
{
$this->username = $username;
return $this;
}
/**
* Set salt
*
* #param string $salt
* #return User
*/
public function setSalt($salt)
{
$this->salt = $salt;
return $this;
}
/**
* Set password
*
* #param string $password
* #return User
*/
public function setPassword($password)
{
$this->password = $password;
return $this;
}
/**
* Set email
*
* #param string $email
* #return User
*/
public function setEmail($email)
{
$this->email = $email;
return $this;
}
/**
* Set isActive
*
* #param boolean $isActive
* #return User
*/
public function setIsActive($isActive)
{
$this->isActive = $isActive;
return $this;
}
/**
* Get isActive
*
* #return boolean
*/
public function getIsActive()
{
return $this->isActive;
}
/**
* Add role
*
* #param \YouMustKnowIt\NewsBundle\Entity\RolesList $role
* #return User
*/
public function addRole(\YouMustKnowIt\NewsBundle\Entity\RolesList $role)
{
$this->role[] = $role;
return $this;
}
/**
* Remove role
*
* #param \YouMustKnowIt\NewsBundle\Entity\RolesList $role
*/
public function removeRole(\YouMustKnowIt\NewsBundle\Entity\RolesList $role)
{
$this->role->removeElement($role);
}
/**
* Add createdNews
*
* #param \YouMustKnowIt\NewsBundle\Entity\NewsCatalog $createdNews
* #return User
*/
public function addCreatedNews(\YouMustKnowIt\NewsBundle\Entity\NewsCatalog $createdNews)
{
$this->createdNews[] = $createdNews;
return $this;
}
/**
* Remove createdNews
*
* #param \YouMustKnowIt\NewsBundle\Entity\NewsCatalog $createdNews
*/
public function removeCreatedNews(\YouMustKnowIt\NewsBundle\Entity\NewsCatalog $createdNews)
{
$this->createdNews->removeElement($createdNews);
}
/**
* Get createdNews
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getCreatedNews()
{
return $this->createdNews;
}
/**
* Get role
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getRole()
{
return $this->role;
}
}
My UserRepository.php class
<?php
namespace YouMustKnowIt\NewsBundle\Entity;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\NoResultException;
class UserRepository extends EntityRepository implements UserProviderInterface
{
public function loadUserByUsername($username)
{
$q = $this
->createQueryBuilder('u')
->select('u, g')
->leftJoin('u.groups', 'g')
->where('u.username = :username OR u.email = :email')
->setParameter('username', $username)
->setParameter('email', $username)
->getQuery();
try {
$user = $q->getSingleResult();
} catch (NoResultException $e) {
$message = sprintf(
'Unable to find an active admin User object identified by "%s".',
$username
);
throw new UsernameNotFoundException($message, 0, $e);
}
return $user;
}
public function refreshUser(UserInterface $user)
{
$class = get_class($user);
if (!$this->supportsClass($class)) {
throw new UnsupportedUserException(
sprintf(
'Instances of "%s" are not supported.',
$class
)
);
}
return $this->find($user->getId());
}
public function supportsClass($class)
{
return $this->getEntityName() === $class
|| is_subclass_of($class, $this->getEntityName());
}
public function findAll()
{
return $this->createQueryBuilder('u');
}
}
SecurityController.php
<?php
namespace YouMustKnowIt\NewsBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\Security\Core\SecurityContext;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Component\HttpFoundation\Request;
use YouMustKnowIt\NewsBundle\Entity\User;
class SecurityController extends Controller
{
/**
* #Route("/login", name="login")
*/
public function loginAction()
{
$request = $this->getRequest();
$session = $request->getSession();
if ($request->attributes->has(SecurityContext::AUTHENTICATION_ERROR)) {
$error = $request->attributes->get(
SecurityContext::AUTHENTICATION_ERROR
);
} else {
$error = $session->get(SecurityContext::AUTHENTICATION_ERROR);
$session->remove(SecurityContext::AUTHENTICATION_ERROR);
}
return $this->render(
'YouMustKnowItNewsBundle:User:login.html.twig',
array(
'last_username' => $session->get(SecurityContext::LAST_USERNAME),
'error' => $error,
)
);
}
/**
* #Route("/login_check", name="login_check")
*/
public function loginCheckAction()
{
}
/**
* #Route("/logout", name="logout")
*/
public function logoutAction()
{
}
/**
* #Route("/recover_pass", name="recover_pass")
*/
public function recoverPasswordAction(Request $request)
{
$data = array();
$form = $this->createFormBuilder($data)
->add('email', 'email')
->getForm();
if ($request->getMethod() == 'POST') {
$form->bind($request);
if ($form->isValid()) {
$data = $form->getData();
$user = $this->getDoctrine()
->getRepository('YouMustKnowItNewsBundle:User')
->findOneByEmail($data['email']);
if (isset($user)) {
$this->createNewPassword($user);
return $this->redirect($this->generateUrl('homepage'));
} else {
$this->get('session')->getFlashbag()->add(
'error_message',
'The user with such email doesn\'t exist.'
);
}
}
}
return $this->render('YouMustKnowItNewsBundle:Default:recoverPass.html.twig', array(
'form' => $form->createView()
));
}
private function sendEmail(User $user)
{
$message = \Swift_Message::newInstance()
->setSubject('YouMustKnowIt! Password restoration.')
->setFrom('php.gr2#gmail.com')
->setTo($user->getEmail())
->setBody('Your new password: ' . $user->getPassword());
$this->get('mailer')->send($message);
}
private function generatePassword($length = 7)
{
$num = range(0, 9);
$alf = range('a', 'z');
$_alf = range('A', 'Z');
$symbols = array_merge($num, $alf, $_alf);
shuffle($symbols);
$code_array = array_slice($symbols, 0, $length);
$code = implode("", $code_array);
return $code;
}
private function encodePassword(User $user)
{
$factory = $this->get('security.encoder_factory');
$encoder = $factory->getEncoder($user);
$password = $encoder->encodePassword(
$user->getPassword(),
$user->getSalt()
);
return $password;
}
private function createNewPassword(User $user)
{
$password = $this->generatePassword();
$user->setPassword($password);
$this->sendEmail($user);
$encodedPassword = $this->encodePassword($user);
$user->setPassword($encodedPassword);
$em = $this->getDoctrine()->getEntityManager();
$em->persist($user);
$em->flush();
$this->get('session')->getFlashbag()->add(
'success_message',
'On your email the new password was sent.'
);
}
}
and finally login.html.twig
{% extends '::base.html.twig' %}
{% block body %}
{% if error %}
<div>{{ error.message }}</div>
{% endif %}
<form action="{{ path('login_check') }}" method="post">
<label for="username">Username:</label>
<input type="text" id="username" name="_username" value="{{ last_username }}" />
<label for="password">Password:</label>
<input type="password" id="password" name="_password" />
#<input type="hidden" name="_csrf_token" value="/" />
<input type="submit" name="login" />
</form>
{% endblock %}
If you are using an entity that is mapped to a database then your Provider: is missing the correct mapping.
providers:
users:
entity: { class: YouMustKnowItNewsBundle:User, property: username }

CakePHP Issue with Search and Pagination

I have a problem with my CakePhp Site.
My index is showing fine with pagination, but when I search the page, pagination is gone and the search results aren't showing (instead the index is showing without paging).
Any idea where my code is wrong?
class ItemsController extends AppController {
var $name = 'Items';
// load any helpers used in the views
var $helpers = array('Paginator', 'Html', 'Form', 'Javascript', 'Misc', 'Time', 'Tagcloud');
var $components = array('RequestHandler');
/**
* index()
* main index page for items
* url: /items/index
*/
function index() {
// get all options for form
$tags = $this->Item->Tag->find('list', array(
'fields'=>'id, name',
'order'=>'Tag.name',
'conditions'=> array(
'Tag.status'=>'1'
)
));
// add name to option
$tags = array(''=>'Tags') + $tags;
//pr($tags);
// if form submitted
if (!empty($this->data)) {
// if reset button pressed redirect to index page
if(isset($this->data['reset'])) {
$this->redirect(array('action'=>'index'));
}
// init
$url = '';
// remove search key if not set
if($this->data['search'] == '') {
unset($this->data['search']);
}
// loop through filters
foreach($this->data as $key=>$filter) {
// ignore submit button
if($key != 'filter') {
// init
$selected = '';
switch($key) {
case 'tag':
$selected = $tags[$filter];
break;
case 'search':
$selected = $filter;
break;
}
// if filter value is not empty
if(!empty($filter)) {
$selected = $this->slug($selected);
$url .= "/$key/$selected";
}
}
}
// redirect
$this->redirect('/items/index/'.$url);
} else {
// set form options
$this->data['tag'] = '';
$this->data['search'] = '';
}
// if any parameters have been passed
if(!empty($this->params['pass'])) {
// only select active items
$conditions = array('Item.status'=>1);
// get params
$params = $this->params['pass'];
// loop
foreach($params as $key=>$param) {
// get the filter value
if(isset($params[$key+1])) {
$value = $params[$key+1];
}
// switch on param
switch($param)
{
case 'tag':
// get tag
$tag = $this->Item->Tag->find('first', array(
'recursive' => 0,
'conditions' => array(
'Tag.slug'=>$value
)
));
// save value for form
$this->data['tag'] = $tag['Tag']['id'];
break;
case 'search':
// setup like clause
$conditions['Item.name LIKE'] = "%{$value}%";
// save search string for form
$this->data['search'] = str_replace('_', ' ', $value);
break;
}
}
//pr($conditions);
// get all items with param conditions
$items = $this->Item->find('all', array(
'order' => 'Item.name',
'conditions' => $conditions
));
// if tag filter has been set
if(isset($tag)) {
// loop through items
foreach($items as $key=>$item) {
// init
$found = FALSE;
// loop through tags
foreach($item['Tag'] as $k=>$g) {
// if the tag id matches the filter tag no need to continue
if($g['id'] == $tag['Tag']['id']) {
$found = TRUE;
break;
}
}
// if the tag was not found in items
if(!$found) {
// remove from list
unset($items[$key]);
}
}
}
} else {
// get all items from database where status = 1, order by name
$items = $this->Item->find('all', array(
'order' => 'Item.name',
'conditions' => array(
'Item.status'=>1
)
));
}
$this->paginate = array(
'limit' => 10
);
$data = $this->paginate('Item');
// set page title
$this->pageTitle = 'Index Page';
// set layout file
$this->layout = 'index';
// save the items in a variable for the view
$this->set(compact('data', 'tags', 'items'));
}
You'll want to pass your search conditions to the paginate functionality. You do this through the controller's paginate property.
function index() {
...
switch($param) {
...
case 'search':
$this->paginate['conditions']['Item.name LIKE'] = "%{$value}%";
More information on setting up pagination can be found here.

Resources