Q: Yii2 integrating Rbac and button discrimination - php

I'm developing a website in php with Yii2 and I have a problem with Rbac issue. I've followed the offical guide, I run the migrations and now I have in my db the four default tables which define my roles and permissions. Now I don't know how to integrate these roles in my project, I mean I would like to have some views only visible to users with specific permissions but can't understand the way to implement this.
I have also a problem with login, I don't know how to discriminate a button click.
login (view):
<div class="form-group">
<div class="col-lg-offset-1 col-lg-11">
<?= Html::submitButton('Login', ['class' => 'btn btn-primary', 'name' => 'login-button', 'value' => 'login']) ?>
<?= Html::submitButton('Register', ['class' => 'btn btn-primary', 'name' => 'register-button', 'value' => 'register']) ?>
</div>
</div>
SiteController:
public function actionLogin()
{
if (!Yii::$app->user->isGuest) {
return $this->goHome();
}
$model = new LoginForm();
if (isset($_POST['submit']) && $_POST['submit']=='login') {
return $this->goBack();
}
if (isset($_POST['submit']) && $_POST['submit']=='register') {
return $this->render('register');
}
return $this->render('login', [
'model' => $model,
]);
}
I just need to render in a different views the user after the right button click. If Login button is clicked I want to be redirected in login view, if Register button is clicked, I want to be redirected in register view.

This seems to be a two-in-one question.
First, RBAC.
This is explained very well in the docs. You can use AccessControl to only allow certain actions to be accessed by a role or permission. If you need to show some content in a view based on a role or permission, use if(Yii::$app->user->can('permission_or_role)) echo "I can"; (docs).
Second, buttons
Check this link, the name of the button must be the name you check for in the controller (not login-button/register-button and check submit).

Related

Laravel Prevent Form from Re submitting

I have a form of Adding Album in database
{!! Form::open(['method' => 'POST', 'route' => ['admin.album.store'], 'enctype' => 'multipart/form-data', 'id' => 'CreateAlbumForm']) !!}
<input type="hidden" name="_token" value="{{ csrf_token() }}">
// other fields
{!! Form::submit(trans('global.app_save'), ['class' => 'btn btn-danger']) !!}
{!! Form::close() !!}
It is working perfectly.
What i need is to prevent user from clicking submit button multiple times. which i know is possible with jquery ( disabling submit button on click).
But i want to make it using csrf protection(Server side) when user does not have javascript enabled.
After a lot of search i found below solution :
What i have tried
Adding Below function in VerifyCsrfToken.php
protected function tokensMatch($request)
{
$token = $request->input('_token') ?: $request->header('X-CSRF-TOKEN');
if (!$token && $header = $request->header('X-XSRF-TOKEN')) {
$token = $this->encrypter->decrypt($header);
}
$tokensMatch = ($request->session()->token() == $token) ? TRUE : FALSE;
if($tokensMatch) $request->session()->regenerateToken();
return $tokensMatch;
}
And adding _token inside $dontFlash array in file app\Http\Requests\FormRequest.php
protected $dontFlash = ['password', 'password_confirmation', '_token'];
It gives me Token Mismatch error But when i click on submit button more than 2 times. And record is inserted 2 times which is unwanted behaviour.
It should give me error on 2nd attempt on submit at same time.
So in short What i need is if a user clicks on submit button single time it should insert record. and if he clicks on submit more than one time than it should give TokenMismatch Error.
You could set a token when you serve the form and check that against the database. When you submit the form, the token is checked and you can't submit it any more. Of course, it is still a good idea to do it front-end too as it is more visual for the user.
https://laracasts.com/discuss/channels/laravel/stopping-multiple-form-submission
Just searching for relevant answer and found this. Hope it will help in some way.

Routes and controller methods for a multi step form are not correct, it appears "The page has expired due to inactivity"

I have a multi step form using bootstrap and jQuery. The multi step form is for a user to buy a product. There are 4 steps:
- Step 1 is for the user insert his info (name, email, etc)
- Step 2 the user select the payment method
- Step 3 the user introduces the payment data
- Step 4 present an invoice for the user.
So I want to divide the form in 4 steps, each step with a respective method in the PaymentController. But the routes or controller methods should have some error because Im getting "The page has expired due to inactivity" when "go to step 2" button is clicked. Do you know where is the error?
The flow of the context is:
The user clicks in a product and goes to the product details page. In the product details page the user select the quantities. So there is a select menu inside a form with this action "<form method="post" action="{{route('products.storeProduct', ['id' => $product->id, 'slug' => $product->slug])}}">".
The route for this form is and to present the payment page:
Route::post('/product/{id}/{slug?}/payment', [
'uses' => 'PaymentController#storeProduct',
'as' =>'products.storeProduct'
]);
Route::get('/product/{id}/{slug?}/payment', [
'uses' => 'PaymentController#presentPaymentPage',
'as' =>'products.payment'
]);
In the PaymentController, the storeProduct method stores the product, get some info from db and then redirect the user to the products.payment route to present the payment page page:
class PaymentController extends Controller
{
public function storeProduct(Request $request, $id, $slug = null){
...
return redirect(route('products.payment',['id' => $id, 'slug' => $slug]));
}
public function presentPaymentPage(Request $request, $id, $slug=null){
...
return view('products.payment',
['product' => $product, 'id' => $id, 'slug' => $slug]);
}
}
So now the user is on the payment page. In this page appears the mult is step form, the first step is step 1 in this first step appears a form for the user introduce somer user info like name, email, etc. The action of the form is for the storeUserInfo() method where the information introduced by the user should be stored in session. Code in the payment.blad.ephp for step 1:
<div class="tab-pane fade show active clearfix" id="step1" role="tabpanel" aria-labelledby="home-tab">
<h6>User Info</h6>
<form method="post" action="{{route('products.storeUserInfo', ['id' => $id, 'slug' => $slug])}}">
<div class="form-group font-size-sm">
<label for="name" class="text-gray">Name</label>
<input type="text" required class="form-control" value="{{ (\Auth::check()) ? Auth::user()->name : old('name')}}">
</div>
<!-- other form fields -->
<input type="submit" href="#step2"
class="btn btn-primary btn float-right next-step" value="Go to step 2"/>
</form>
</div>
In the PaymentController I added two methods:
public function storeUserInfo(Request $request, $id, $slug = null){
dd($request->all());
}
public function presentPaymentMethods(Request $request, $id, $slug=null){
}
And in web.php two routes:
Route::post('/product/{id}/{slug?}/payment/storeUserInfo', [
'uses' => 'PaymentController#storeUserInfo',
'as' =>'products.storeUserInfo'
]);
Route::get('/product/{id}/{slug?}/payment/paymentMethods', [
'uses' => 'PaymentController#presentPaymentMethods',
'as' => 'products.presentPaymentMethods'
]);
When user click in "Go to step 2" he is redirected to ""store.test/product/5/a-5/payment/storeUserInfo";" but it appears "The page has expired due to inactivity. Please refresh and try again". Do you know where is the issue?
You may have missed the CSRF protection as I can't see it on your form: https://laravel.com/docs/5.6/csrf

How to apply url in Yii framework?

I have a page (commerce.php), I want to give the link to the below-mentioned button, how I do that in Yii framework? My page path is frontend/views/site/commerce.php.
Below code is belong from index.php page
<button class="btn"><h5>Marketing Team</h5>
try the following
<?= \yii\helpers\Html::a(
'Marketing Team', ['site/market'], ['class' => 'btn btn-primary']
); ?>

Laravel 5:Validation on update to model with relationship to user is throwing unique error

I was doing great so far until I ran into another problem with validation. I'm trying to update a user related table called socialLinks. The problem is every time I update, since it's a model backed form, the prefilled values in the form gets pass through validations and I get a 'has already been taken' error in return.
After doing a bunch of googling, I tried to pass userId through update, but i haven't had any success. I had this problem before but I was validating a column from User. Now I'm trying to validate a column from another table with a relationship with user and nothing I tried before works. Super Frustrating.
my form
{!! Form::model($user_links,['method' => 'PATCH', 'action'=> ['UserController#update_social']]) !!}
<div class='row form-group'>
<div class='col-md-2'>
{!! Form::label('facebook', 'Facebook Username') !!}
</div>
<div class='col-md-7'>
{!! Form::text('facebook', null,['class'=>'form-control']) !!}
</div>
</div>
<div class='row form-group'>
<div class='col-md-2'>
{!! Form::label('twitter', 'Twitter Username') !!}
</div>
<div class='col-md-7'>
{!! Form::text('twitter', null,['class'=>'form-control']) !!}
</div>
</div>
<div class='row form-group'>
<div class='col-md-2'>
{!! Form::label('reddit', 'Reddit Username') !!}
</div>
<div class='col-md-7'>
{!! Form::text('reddit', null,['class'=>'form-control']) !!}
</div>
</div>
<div class='row form-group'>
<div class='col-md-3'>
{!! Form::submit('Save Changes',['class'=>'btn btn-md btn-success']) !!}
{!! Form::close() !!}
routes
Route::get('/account/social','UserController#social');
Route::patch('/account/social','UserController#update_social');
and controllers
public function social(Request $request){
$user = $request->user();
$user_links= $request->user()->links;
return view('user.edit.social_connect',compact('user_links','user'));
}
public function update_social(Request $request){
$user = $request->user();
$validator=Validator::make($request->all(),[
'facebook' => 'unique:social_links,facebook,'.$user->id,
'twitter' => 'unique:social_links,twitter'.$user->id,
'reddit' => 'unique:social_links,reddit,'.$user->id,
'google' => 'unique:social_links,google,'.$user->id
]);
if ($validator->fails()){
var_dump($user->id);exit;
return Redirect::back()->withErrors($validator)->withInput();
}
$data=Input::all();
$links = $user->links;
if ($links == null){
$links = new SocialLinks();
$links->fill($data);
$links->user_id=$user->id;
$links->save();
}else{
$links->fill($data);
$links->save();
}
return Redirect::back()->with('message','Your profile has been updated');
}
Update put validation logic in controller
this is what worked for me
$user = $request->user();
$id = $user->id;
$validator=Validator::make($request->all(),[
'facebook' => 'unique:social_links,facebook,'.$id.',user_id',
'twitter' => 'unique:social_links,twitter,'.$id.',user_id',
'reddit' => 'unique:social_links,reddit,'.$id.',user_id',
'google' => 'unique:social_links,google,'.$id.',user_id',
]);
I honestly don't get why though. Why do we concatenate id to exclude it from being validated? the syntax makes no sense to me. Also $id comes from user object, and 'user_id' is from my social links table. Am I matching ids here? Somebody please show me the light ;(
To start: In general, extending the Laravel Request class and dependency injecting your extended class into your controller functions, although it can be done, is a bad idea. Read this to understand why (although it may not appear to be related at first read):
Laravel 5 / Codeception not routing correctly
Basically the problem is that if your validation class throws a perfectly legitimate validation exception, that exception has to be trapped by the Laravel router and not by the controller function. Essentially unless the validation passes your controller function never gets called, and so it can't trap the validation exception. This frequently results in either a 500 error or a redirect to a completely different page (which is why this screws up codeception tests amongst other unwanted side-effects).
So a better plan is to pass a Laravel Request object into your controller function and validate inside the function, rather than attempting to get Laravel to do it for you.
Once you resolve that issue, getting the validation code to handle the case where the $user does not exist should be easy. Some suggestions for your validation code to handle that validation (which should now be inside your controller function, or in a separate validator class called from your controller function):
Request probably doesn't contain a User object. It might contain a user_id variable which you can turn into a User object using User::find($user_id) or similar.
Check to ensure that $user is not null before attempting to work with $user->id or you'll get an error. If the $user_id passed in is empty or invalid then User::find($user_id) will return null. Check for this case and throw an exception first, before you continue to look at $userId or save any links.
I can't see exactly what you're doing in your form but if the above doesn't resolve your issues then post the form here and we can take another look.
You need to edit validation for updating, this is my example from one app :
public function rulesAdmin($id = NULL)
{
return [
'name' => 'required|min:5',
'username' => 'unique:users' . ($id ? ",username, $id" : ''),
];
}
on editing ->you pass from database $id, on saving new record -> $id is NULL.

Passing data from view to controller in yii

I have a text field in my view and I want to pass the value of textbox to the controller, but I don't know how to do it.
I have tried googling it but it only gives ides about passing data from conntroller to view in yii, so please give an example of doing this with ajax.
follow the steps:
in form:
<div class="form-group">
<?php echo $form->labelEx($model,'order_id', array('class' => 'control-label col-lg-4')); ?>
<div class="col-lg-8">
<?php echo $form->textField($model,'order_id',array('class' => 'form-control',
'ajax' =>
array('type'=>'POST',
'url'=>$this->createUrl('recieveValue'), // write in controller this action
'update'=>'#price',
'data'=>array('order_id'=>'js:this.value'),
)
)); ?>
</div>
<?php echo $form->error($model,'order_id'); ?>
In controller:
public function actionRecieveValue(){
echo $_POST['order_id'];
}
At the top of same controller:
array('allow', // allow authenticated user to perform 'create' and 'update' actions
'actions'=>array('create','update','recieveValue'),
'users'=>array('#'),
),
Explanation:
Here text field id is order_id, controller action is recieveValuewhat I wrote in the ajax URL as 'url'=>$this->createUrl('recieveValue'),. Go to the controller and write action name as actionRecieveValue just add action before recieveValue. Now go to the top of the controller in the method accessRules and allow it recieveValue into array. Now check through firebug console. Type something into the text box and move the mouse from the textbox. You will find that your textbox value will be recieved into the controller.

Categories