How to apply rules on update in yii2 - yii

I am new to yii2 and I don't exactly know how to apply same rules of insert into update. Here is my rules for insert and I want all rules to effect on update too.
public $tag;
public function rules()
{
return [
[['user_id', 'name', 'address', 'state'], 'required'],
[['user_id'], 'integer'],
[['tag'], 'safe'],
[['name'], 'string', 'max' => 30],
[['address'], 'string', 'max' => 250],
[['state'], 'string', 'max' => 255],
];
}
Adding 'on' => 'update' doesn't effect in my code. Sorry for my english. Thanks.

The default rules are applied in the creation and update, you only have to create new rules if you want different behaviors between update and create.
To apply the rules just make a $model->validate() to check the rules, see this example of the documentation of yii2
$model->load(\Yii::$app->request->post());
if ($model->validate()) {
// all inputs are valid
} else {
// validation failed: $errors is an array containing error messages
$errors = $model->errors;
}

You can add validation rules to yii model like this
public function rules(){
return [
[['boolean_var'],'boolean'],
[[ 'range_in_string'],'required'],
[['default_value'],'default','value'=>'DEFAULT VALUE'],
[['integer_var'],'integer'],
[['double_var'],'double'],
[['number_min'],'number','min'=>10],
[['number_max'],'number','max'=>100],
[['number_min_max'],'number','min'=>10,'max'=>100],
[['file_image'],'image'],
[['file_var'],'file','types'=>['gif','jpg']],
[['date_var'],'date', 'format'=>'d-m-yy'],
[['min_string'],'string','min'=>10],
[['max_string'],'string','max'=>10],
[['min_max_string'],'string','min'=>5,'max'=>10],
['min_max_string2', 'string', 'length' => [4, 10]],
['email_var','email'],
['url_var','url'],
[['unique_var'],'unique'],
[['filter_trim'],'filter','filter'=>'trim'],
[['filter_trim'],'filter','filter'=>'strtolower'],
['filter_custom_function', 'filter', 'filter' => function ($value) {
// your concept
if($value=='')
return '';
return 'Value Changed';}],
['range_in_min_max', 'in','range'=>range(5,20)],
['range_in','in','range'=>['en','fr','zn'],'strict'=>false], //strict false
['range_in_string','in','range'=>['EN','FR','ZN'],'strict'=>true],
['custom_validation','custom_function_validation'],
[['passwordConfirm'], 'compare', 'compareAttribute' => 'password'],
[['password','date_var'],'safe'],
];
}
if you need to trigger only when updating but not creating, you can add a scenario like this
public function rules()
{
return [
// username, email and password are all required in "update" scenario
[['username', 'email', 'password'], 'required', 'on' => 'update'],
// username and password are required in "login" scenario
[['username', 'password'], 'required', 'on' => 'login'],
];
}
and in your action before calling validate add the scenario
// scenario is set as a property
$model = new User;
$model->scenario = 'update';

Related

How to remove validation lastname from prestashop 1.7.8.3 backoffice

I have a question how to remove validation from LastName inside client address edit. I need to allow numbers inside this field.
I found here thread Prestashop : Remove Lastname Field Rules Validation From B.O, but this solution is not working.
Finally, I have caught the issue. You are editing in admin panel and I was sharing code for front end. Please try below steps for admin:
Step 1 - file classes/Address.php
'lastname' => ['type' => self::TYPE_STRING, 'validate' => 'isAnything', 'required' => true, 'size' => 255],
Change this to isAnything
Step 2 - src\PrestaShopBundle\Form\Admin\Sell\Address/CustomerAddressType.php
Change your code to below code:
line 209: add('last_name', TextType::class, [
'label' => $this->trans('Last name', 'Admin.Global'),
'help' => $genericInvalidCharsMessage,
'required' => true,
'constraints' => [
new NotBlank([
'message' => $this->trans(
'This field cannot be empty.', 'Admin.Notifications.Error'
),
]),
new CleanHtml(),
new TypedRegex([
'type' => TypedRegex::TYPE_GENERIC_NAME,
]),
new Length([
'max' => AddressConstraint::MAX_LAST_NAME_LENGTH,
'maxMessage' => $this->trans(
'This field cannot be longer than %limit% characters',
'Admin.Notifications.Error',
['%limit%' => AddressConstraint::MAX_LAST_NAME_LENGTH]
),
]),
],
])
Now, you are ready to go and check.
Go to the file classes/Address.php file:
'lastname' =>array('type' => self::TYPE_STRING, 'validate' => 'isCustomerName', 'required' => true, 'size' => 32),
to :
'lastname' =>array('type' => self::TYPE_STRING, 'validate' => 'isAnything', 'required' => true, 'size' => 32),
validate to isAnything.
I think you were modifying in customer class. Please try with Address.php.
Thanks for sharing the files.
I have resolved the case. You need to modify the classes/form/CustomerAddressForm.php
line 229
$isValid &= $this->validateField('lastname', 'isName', $this->translator->trans(
'Invalid name',
[],
'Shop.Forms.Errors'
));
Change to:
$isValid &= $this->validateField('lastname', 'isAnything', $this->translator->trans(
'Invalid name',
[],
'Shop.Forms.Errors'
));
I want to do this good with override. I have an issue with override this class. I have created module to override but it is not working. There is a way to override this without editing core files?
services:
_defaults:
public: true
form.type.customer_address:
class: 'Playdev\PrestaShopBundle\Form\Admin\Sell\Address\CustomCustomerAddressType'
public: true
arguments:
- '#prestashop.adapter.form.choice_provider.country_state_by_id'
- '#=service("prestashop.adapter.legacy.context").getContext().country.id'
- '#router'
tags:
- { name: form.type }
https://ibb.co/VVjnJYr
There is a file class override:
\modules\pd_overridemodule\src\PrestaShopBundle\Form\Admin\Sell\Address\CustomCustomerAddressType.php
https://ibb.co/7QPHrqx
And I have an error when I am inside Edit Address Form Backoffice
Type error: Too few arguments to function PrestaShopBundle\Form\Admin\Sell\Address\CustomerAddressType::__construct(), 0 passed in C:\laragon\www\prestabiolab\vendor\symfony\symfony\src\Symfony\Component\Form\FormRegistry.php on line 92 and exactly 5 expected
[Symfony\Component\Debug\Exception\FatalThrowableError 0]
https://ibb.co/YfwhtKq
I have found a solution
Need to create module and call hookactionCustomerAddressFormBuilderModifier.
public function hookactionCustomerAddressFormBuilderModifier(array $params)
{
/** #var $formBuilder \Symfony\Component\Form */
$formBuilder = $params['form_builder'];
// remove lastname field
$formBuilder->remove('last_name');
// get all fields without removed
$allFields = $formBuilder->all();
// remove all fields
foreach ($allFields as $inputField => $input) {
$formBuilder->remove($inputField);
}
foreach ($allFields as $inputField => $input) {
// normally add fields
$formBuilder->add($input);
// add fields after firstname
if ($inputField == 'first_name') {
$formBuilder->add('last_name', TextType::class, [
'label' => $this->trans('Last name', [], 'Admin.Global'),
'help' => $this->trans(
'Invalid characters:',
[],
'Admin.Notifications.Info'
) . ' ' . TypedRegexValidator::GENERIC_NAME_CHARS,
'required' => true,
'constraints' => [
new NotBlank([
'message' => $this->trans(
'This field cannot be empty.', [], 'Admin.Notifications.Error'
),
]),
new CleanHtml(),
new TypedRegex([
'type' => TypedRegex::TYPE_GENERIC_NAME,
]),
new Length([
'max' => AddressConstraint::MAX_LAST_NAME_LENGTH,
'maxMessage' => $this->trans(
'This field cannot be longer than %limit% characters',
['%limit%' => AddressConstraint::MAX_LAST_NAME_LENGTH],
'Admin.Notifications.Error',
),
]),
],
]);
}
}
}
Now I think it works okey with override :)

Laravel 8: Extending UserCrudController from Package BackPack Permission-Manager

I want to add Department field in User entity, so I decided to extend the default UserCrudController from this package https://github.com/Laravel-Backpack/PermissionManager.
So I created a custom controller with this command php artisan make:controller Admin\UserController
In file \app\Providers\AppServiceProvider.php I also add this (as instructed)
public function register()
{
$this->app->bind(
\Backpack\PermissionManager\app\Http\Controllers\UserCrudController::class,
\App\Http\Controllers\Admin\UserController::class,
);
}
Then here is the content of \App\Http\Controllers\Admin\UserController
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Backpack\PermissionManager\app\Http\Controllers\UserCrudController;
use Backpack\CRUD\app\Library\CrudPanel\CrudPanelFacade as CRUD;
class UserController extends UserCrudController
{
public function setupCreateOperation()
{
parent::setupCreateOperation();
$fields['departments'] = [
'label' => 'Departments',
'type' => 'select2_multiple',
'name' => 'tags',
'entity' => 'tags',
'attribute' => 'name',
'model' => "App\Models\Tag",
'pivot' => true,
'wrapper' => ['class' => 'form-group col-6 col-md-4'],
'options' => (function ($query) {
return $query->where('type', 'Department')->get();
}),
];
foreach($fields as $key=>$field) {
CRUD::addField($field);
}
}
public function setupUpdateOperation()
{
parent::setupUpdateOperation();
$this->setupCreateOperation();
}
}
All seems fine, I can see all default information such as: username, email, roles, permission and my custom department fields.
The problem is, when I try to modify an existing user adding some Departments, I got this error
The email has already been taken.
The password field is required.
Somehow it's treated as new user registration. What could be the problems?
Somehow, I solved it by copying setupCreateOperation to setupUpdateOperation.
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Backpack\PermissionManager\app\Http\Controllers\UserCrudController;
use Backpack\CRUD\app\Library\CrudPanel\CrudPanelFacade as CRUD;
class UserController extends UserCrudController
{
...
public function setupUpdateOperation()
{
parent::setupUpdateOperation();
$fields['departments'] = [
'label' => 'Departments',
'type' => 'select2_multiple',
'name' => 'tags',
'entity' => 'tags',
'attribute' => 'name',
'model' => "App\Models\Tag",
'pivot' => true,
'wrapper' => ['class' => 'form-group col-6 col-md-4'],
'options' => (function ($query) {
return $query->where('type', 'Department')->get();
}),
];
foreach($fields as $key=>$field) {
CRUD::addField($field);
}
}
}
Hopefully there's a shorter way to reduce this duplication.

cakephp 3.8.13 add admad/cakephp-jwt-auth

This question is asked many times in the stack overflow but I tried every accepted solution.
I'm new to cake PHP and I was assigned to add JWT in our application. Previously the team used the default cake sessions. In order to integrate, I used admad/cakephp-jwt-auth. So In the AppController
public function initialize()
{
parent::initialize();
$this->loadComponent('RequestHandler');
$this->loadComponent('Flash');
$this->loadComponent('Recurring');
$this->loadComponent('Auth', [
'storage' => 'Memory',
'authenticate' => [
'Form' => [
'fields' => [
'username' => 'user_name',
'password' => 'password',
],
'contain' => ['Roles']
],
'ADmad/JwtAuth.Jwt' => [
'parameter' => 'token',
'userModel' => 'CbEmployees',
'fields' => [
'username' => 'id'
],
'queryDatasource' => true
]
],
'unauthorizedRedirect' => false,
'checkAuthIn' => 'Controller.initialize'
]);
}
I have to use CbEmployees which is our user model.
Then in my custom controller, I add my login function
public function login()
{
$user = $this->Auth->identify();
if (!$user) {
$data = "Invalid login details";
} else {
$tokenId = base64_encode(32);
$issuedAt = time();
$key = Security::salt();
$data = JWT::encode(
[
'alg' => 'HS256',
'id' => $user['id'],
'sub' => $user['id'],
'iat' => time(),
'exp' => time() + 86400,
],
$key
);
}
$this->ApiResponse([
"data" => $data
]);
}
Then I call this function using postman with body
{
"username": "developer",
"password": "dev2020"
}
I always get the response as Invalid login details. So the suggested solution is to check the password data type and length. The password is varchar(255). Another solution is to check the password in the entity. In the entity I have
protected function _setPassword($password)
{
if (strlen($password) > 0) {
return Security::hash($password, 'sha1', true);
// return (new DefaultPasswordHasher)->hash($password);
}
}
I specifically asked why the team is using Security::hash($password, 'sha1', true); due to migration from cake 2 to cake 3 they have to use the same.
Why I'm getting always Invalid login details? What I'm doing wrong here? I can log in the using the same credentials when I'm using the application.

Access control of a Module in Yii2

I am having a trouble with login part.
I read this topic : http://www.yiiframework.com/wiki/771/rbac-super-simple-with-admin-and-user/ .
Then i follow its steps, but in step 6. it only configs for just one Controller. I have a Module called Admin with many controllers in it and i don't know how to apply this access control to the whole module. Can anyone help me ?
Sorry for my bad English.
You can create AdminController class, which will extends yii\web\Controller where you define your access rules in behaviors method and make other module controllers extend your AdminController and override behaviors method like this:
public function behaviors()
{
return \yii\helpers\ArrayHelper::merge(parent::behaviors(), [
'verbs' => [
'class' => VerbFilter::className(),
'actions' => [
'delete' => ['post'],
],
],
]);
}
Here parent::behaviors() are behaviors from AdminController which define default access rules, and you merge them with specific behaviors in your child controller. It gives you flexibility to override some access rules if you need.
I can propose a variation of the method from the article that you mentioned.
Make first 2 steps as it was described and then do the following:
1. Add the field role to User model and evaluate it with thevalue of one of the constants from the article's example (User::ROLE_ADMIN or User::ROLE_USER)
2. Override the yii\web\User->can()
public function can($permissionName, $params = [], $allowCaching = true)
{
/** #var \app\models\User $user */
$user = $this->identity;
$access = false;
do {
if (\Yii::$app->user->isGuest) {
break;
}
if ($user->role === \common\models\User::ROLE_ADMIN) {
$access = true;
break;
}
if (is_array($permissionName)) {
$access = in_array($user->role, $permissionName);
} else {
$access = $permissionName === $user->role;
}
} while (false);
return $access;
}
So now you can check user's role like this:
\Yii::$app->user->can(User::ROLE_USER)
3. You say:
i don't know how to apply this access control to the whole module.
Then open your module class and add the following to the behaviors() method:
public function behaviors()
{
return [
'access' => [
'class' => AccessControl::className(),
'rules' => [
[
'allow' => true,
'roles' => [User::ROLE_ADMIN]
]
]
]
];
}
In this example we grant access to ROLE_ADMIN to all actions of all controllers of your module.
That's it.
Make a custom model AccessRules.php as shown below:
<?php
namespace app\models;
class AccessRules extends \yii\filters\AccessRule
{
/**
* #inheritdoc
*/
protected function matchRole($user)
{
if (empty($this->roles)) {
return true;
}
foreach ($this->roles as $role) {
if ($role === '?') {
if ($user->getIsGuest()) {
return true;
}
} elseif ($role === '#') {
if (!$user->getIsGuest()) {
return true;
}
// Check if the user is logged in, and the roles match
} elseif (!$user->getIsGuest() && (int)$role === $user->identity->user_role) {
return true;
}
}
return false;
}
}
?>
Now open your site controller and add the following code in fuction behavior part:
use app\models\AccessRules;
public function behaviors()
{
return [
'access' => [
'class' => AccessControl::className(),
// We will override the default rule config with the new AccessRule class
'ruleConfig' => [
'class' => AccessRules::className(),
],
'only' => ['create', 'update', 'delete','index'],
'rules' => [
[
'actions' => ['create', 'update', 'delete','index'],
'allow' => true,
// Allow admin to create
'roles' => [
'1'
],
]
],
],
'verbs' => [
'class' => VerbFilter::className(),
'actions' => [
'logout' => ['post'],
],
],
];
}
According to The Yii2 Guide
"ACF is an action filter that can be used in a controller or a module" in the same way.
Just add below code in controller which you want to restrict functionality
'access' => [
'class' => AccessControl::className(),
'rules' =>
[
[
'actions' => ['index','view'],
'allow' => true,
'roles' => ['#']
],
[
'actions' => ['create','update','delete'],
'allow' => true,
'roles' => ['#'],
'matchCallback' => function ($rule, $action)
{
return Admin::isUserAdmin(Yii::$app->user->identity->username);
}
],
],
],

CakePHP 2, how to AuthComponent::login() with plain-text password?

I would like to implement CakePHP 2 website over existing database with plain-text password field.
This is my AppController
class AppController extends Controller {
public $components = array(
'Session',
'Auth' => array(
'loginRedirect' => array('controller' => 'users', 'action' => 'index'),
'logoutRedirect' => array('controller' => 'users', 'action' => 'home'),
'authError' => 'You cannot view this page',
'authorize' => array('Controller'),
'authenticate' => array(
'Form' => array(
'userModel' => 'User',
'fields' => array('username' => 'user_id', 'password' => 'user_password')
)
)
)
);
public function isAuthorized($user) {
return true;
}
function beforeFilter() {
$this->Auth->allow('home');
//$this->Auth->authenticate = $this->User;
parent::beforeFilter();
}
This is my UserController.
class UsersController extends AppController {
public $paginate = array(
'fields' => array('user_id', 'user_desc', 'user_password'),
'limit' => 25,
'order' => array(
'user_id' => 'asc'
)
);
function login() {
if ($this->request->is('post')) {
if ($this->Auth->login()) {
$this->redirect($this->Auth->redirect());
} else {
$this->Session->setFlash('Cannot Login');
}
}
}
}
This is my User model
class User extends AppModel {
public $name = 'User';
public $primaryKey = 'user_id';
public $belongsTo = 'Group';
}
According to those files above, when I pressed the Login button on login.ctp, I saw
select * from users where user_password = 'this_is_hashing_password'
on the sql dump section.
So, how to turn-off the automatic hashing algorithm, so the login() will compare the user input to the database stored password as plain-text???
I have tried lots of reading on the CakePHP book but I cannot find any, also using hashPasswords($data) technique which found from the internet is not working.
Please help.
Kongthap.
The best answer really is to batch-process your stored passwords so they are hashed, however there are cases where you may be adding a Cake app to an existing application that hashes passwords differently (say by not hashing them at all), so the question is valid even if the goal in this case is not.
Try these resources for modifying Cake's password hashing function, depending on your Cake version:
Cake 2.x
Cake 1.3
I got plain text password working in Cakephp 3, this should only be use for DEVELOPMENT purpose, you should never store password in plain text in production.
That being said, during development, plain text password allows me to focus on login instead of implementing a fully functional user encrypt/decrypt logic. Which is going to be replaced by an OAuth / SAML module anyway...
OK here comes the source code:
ROOT/src/Auth/PlainTextPasswordHasher.php
<?php
namespace App\Auth;
use Cake\Auth\AbstractPasswordHasher;
/**
* Plain text password for demo use, DO NOT PUSTH THIS TO PROD
*/
class PlainTextPasswordHasher extends AbstractPasswordHasher
{
public function hash($password)
{
return $password;
}
public function check($password, $hashedPassword)
{
return $password === $hashedPassword;
}
}
ROOT/src/Controller/PagesController.php
<?php
class PagesController extends AppController
{
public function initialize()
{
parent::initialize();
$this->loadComponent('Auth', [
'authenticate' => [
'Form' => [
'fields' => [
'username' => 'username',
'password' => 'password',
],
'passwordHasher' => [
'className' => 'PlainText',
],
'userModel' => 'YourUsers',
]
],
'loginAction' => [
'controller' => 'Logins',
'action' => 'login'
]
]);
}
}
Source: This video https://www.youtube.com/watch?v=eASSNS1f3V4 and this section of the official doc: https://book.cakephp.org/3.0/en/controllers/components/authentication.html#creating-custom-password-hasher-classes