CakePHP3.x: authentication required popup - cakephp-3.x

Sometimes, maybe when I stay long time enough without action, or when I try to access directly to a specific page, an "authentication required" popup is displayed asking for my login and password.
But even if I fill in the required fields, the authentication is not done and the refresh just display "Unauthorized.
It seems this popup doesn't call my login method.
Can you tell me how the credentials are supposed to be transmited to my user login controller?
Regards,
EDIT My login Form
<div class="container-fluid">
<br/>
<div class="container-fluid col-md-4 col-md-offset-4">
<h1>Se connecter</h1>
<div class="row">
<div class="col-md-12">
<?= $this->FormB->create(); ?>
<div class="panel panel-default">
<div class="panel-heading">
<h2><?= __("Saisissez vos identifiants") ?></h2>
</div>
<div class="panel-body">
<?= $this->FormB->input('username', ['label' => __("Nom d'utilisateur :"), 'placeholder' => __("Nom d'utilisateur")]); ?>
<?= $this->FormB->input('password', ['label' => __("Mot de passe :"), 'placeholder' => __("Mot de passe")]); ?>
<p><em><?= $this->Html->link(__("Mot de passe oubliƩ ?"), ['action' => 'forgotPassword']); ?></em></p>
<br/>
<?php if ($activeLink == 1): ?>
<p><em><?= $this->Html->link(__("Renvoyer l'email d'activation ?"), ['controller' => 'Users', 'action' => 'resend', 'username' => $username]); ?></em></p>
<br/>
<?php endif ?>
</div>
</div>
<div>
<?= $this->Form->button(isset($btnlabel) ? __("$btnlabel") : __("Enregistrer"), ["class" => "btn btn-primary btn-block"]) ?>
</div>
<?= $this->FormB->end() ?>
</div>
</div>
</div>
</div>
** EDIT ** When I enter my login/password in the popup opened by the browser, Cake generates the following error
2016-03-24 16:53:11 Error: [Cake\Network\Exception\UnauthorizedException] Unauthorized
Request URL: /pros/Owners/account
Stack Trace:
#0 /home/MyApp/vendor/cakephp/cakephp/src/Controller/Component/AuthComponent.php(359): Cake\Auth\BasicAuthenticate->unauthenticated(Object(Cake\Network\Request), Object(Cake\Network\Response))
#1 /home/MyApp/vendor/cakephp/cakephp/src/Controller/Component/AuthComponent.php(296): Cake\Controller\Component\AuthComponent->_unauthenticated(Object(App\Controller\Pros\OwnersController))
#2 /home/MyApp/vendor/cakephp/cakephp/src/Controller/Component/AuthComponent.php(258): Cake\Controller\Component\AuthComponent->authCheck(Object(Cake\Event\Event))
#3 /home/MyApp/vendor/cakephp/cakephp/src/Event/EventManager.php(385): Cake\Controller\Component\AuthComponent->startup(Object(Cake\Event\Event))
#4 /home/MyApp/vendor/cakephp/cakephp/src/Event/EventManager.php(355): Cake\Event\EventManager->_callListener(Array, Object(Cake\Event\Event))
#5 /home/MyApp/vendor/cakephp/cakephp/src/Event/EventDispatcherTrait.php(78): Cake\Event\EventManager->dispatch(Object(Cake\Event\Event))
#6 /home/MyApp/vendor/cakephp/cakephp/src/Controller/Controller.php(495): Cake\Controller\Controller->dispatchEvent('Controller.star...')
#7 /home/MyApp/vendor/cakephp/cakephp/src/Routing/Dispatcher.php(109): Cake\Controller\Controller->startupProcess()
#8 /home/MyApp/vendor/cakephp/cakephp/src/Routing/Dispatcher.php(87): Cake\Routing\Dispatcher->_invoke(Object(App\Controller\Pros\OwnersController))
#9 /home/MyApp/webroot/index.php(37): Cake\Routing\Dispatcher->dispatch(Object(Cake\Network\Request), Object(Cake\Network\Response))
#10 {main}
So how am I supposed to intercept this authentication attempt?

The problem was caused by a modification I made in AppController to authenticate from API request:
Normally, you should load Auth component like that:
$this->loadComponent('Auth', [
'authorize' => ['Controller'],
'authenticate' => [
'Form' => [
'scope' => ['Users.active' => 1]
],
],
'loginRedirect' => [
'controller' => 'Users',
'action' => 'account'
],
'logoutRedirect' => [
'controller' => 'Index',
'action' => 'index'
]
]);
That way, you authenticate filling your credential in a Form.
Another way is to use Basic authentication. it's less used but it can be implemented like that.
$this->loadComponent('Auth', [
'authorize' => ['Controller'],
'authenticate' => [
'Basic' => [
'fields' => ['username' => 'username', 'password' => 'password'],
'userModel' => 'Users'
],
],
'storage' => 'Memory',
'unauthorizedRedirect' => false
]);
Doing like that, causes the login popup to be displayed if you try to access a web page.
I don't know if loading both Form and Basic authentication really makes sense, but it's what I did first and it's because I sometimes saw the popup displayed, notably when the session authenticated by Form expired.
If you need a normal authentication mechanism for your website, load Auth component with Form in your AppController.php.
If you need a basic authentication mechanism for API access, I advise to create an ApiController.php where you will load Auth component with Basic as given above. Then, you API controller will extend ApiController instead of AppController.
It's what I can say from my knowledge. CakePHP guru could add more infos or correct me if necessary.

I have completely same issue as post. I was considered helping by someone and I fixed that problem.
My config auth component before:
$this->loadComponent('Auth', [
'authenticate' => array(
'Form' => array(
// 'fields' => array('username' => 'email'),
'scope' => array('is_delete' => '0')
)
),
and after I edited it, it was run perfectly.
$this->loadComponent('Auth', [
'authenticate' => array(
'Form' => array(
'userModel' => 'MUsers', //Add this line
'fields' => array('username' => 'username',
'password' => 'password'), //Edited this line
'scope' => array('is_delete' => '0')
)
),
If someone want to know to to fix please review my post at:
Cakephp 3 - Auth session cannot destroy

Related

Codeigniter 4 - Apply validation rules without making the fields required for URL

I want to apply validation rules only for the input fields that are not empty (not required) for the URL
For example,
If I submit a form and the input is empty, I got a validation error "Instagram Link must be valid URL.", However, I want it without requiring,
and if the input is not empty, I want to apply the rule "valid_url"
How can we fix it?
if (!$this->validate([
'instagram' => [
'rules' => 'valid_url',
'errors' => [
'valid_url' => 'Instagram Link must be valid url.',
],
],
])){
return redirect()->back()->withInput()->with('errors', $this->validator->getErrors());
}
I tried with a permit_link rule, but if I submit it (with an input value like 'mylink' (which is not a valid_url)), it will accept it, but it should not.
Please check the following images and the code:
A form HTML
Result after clicking on edit button
<?= form_open('/settings/edit/1', ['id' => 'setting-form']); ?>
<div class="row">
<div class="col-md-6">
<div class="form-group">
<?= form_label('Instagram'); ?>
<?= form_input(['name' => 'instagram', 'class' => 'form-control', 'id' => 'instagram', 'placeholder' => 'Enter instagram link', 'value' => old('instagram', $setting->instagram)]); ?>
</div>
</div>
</div>
<button type="submit" class="btn btn-dark">Edit</button>
<?= form_close(); ?>
public function edit($id)
{
if (!$this->validate([
'instagram' => [
'rules' => 'permit_empty|valid_url',
'errors' => [
'valid_url' => 'Instagram Link must be valid url.',
],
],
])) {
return redirect()->back()->withInput()->with('errors', $this->validator->getErrors());
}
die('submitted');
}
It should display "Instagram Link must be valid url" and not "submitted",
first
Use separate variable $rules like this :
$rules = ['data' => 'require']
Then
check if $this->request->getVar("instagram"); is empty / is true or etc.. then set it on the $rules
finally
Do Something like this :
$rules = ['extra_data' => 'require'];
if(!empty($this->request->getVar("instagram"))
$rules["instagram"] = "valid_url";
if ($this->validate(rules){
//do something ...
}else {
//do something ...
}
I just noticed that the following examples:
"mylink", "mylink.com", "https://www.mylink.com"
will consider correct for the rule valid_url in Codeigniter (No errors),
While: "https:://www.mylink.com", "mylink#" will apply the validation and the error is applied.

How can I create popup form in Yii2

I want a way to create popup form when I press the button using Ajax and bootstrap
and how to create modal for it with bootstrap
use yii\bootstrap\Modal;
you can do it like it, call a view/action in modal
<?php
Modal::begin([
'toggleButton' => [
'label' => '<i class="fa fa-plus"></i> Add',
'class' => 'btn btn-success'
],
'closeButton' => [
'label' => 'Close',
'class' => 'btn btn-danger btn-sm pull-right',
],
'size' => 'modal-lg',
]);
$myModel = new \frontend\models\SomeModel;
echo $this->render('/someview/create', ['model' => $myModel]);
Modal::end();
?>
reference Render Form in popup via AJAX

Yii2: How to add an image as a navbar menuitem

i'm trying without success to add an image inside a Navbar menuItem.
It works fine in the brandLabel
But doesn't work on anoter menu item
(I Want to display the logged user image near his name)
I Tryed several times but doesnt seem to make it work,
Would apreciate your help please...
The Code is this:
<?php $this->beginBody() ?>
<div class="wrap">
<?php
NavBar::begin([
'brandLabel' => Html::img('#themes'.'/siteImages/Logo.jpg', ['alt'=>Yii::$app->name]),
'options' => [
'class' => 'navbar-default navbar-fixed-top',
],
]);
// display Login page to guests of the site
if (Yii::$app->user->isGuest) {
$menuItems[] = ['label' => Yii::t('app', 'Login'), 'url' => ['/site/login'],
'options' => [
'class' => 'navbar-right',
],];
}
else
{
// Show sales content to sales+ users
if ( Yii::$app->user->can('useSalesContent'))
{
$menuItems[] = ['label' => Yii::t('app', 'Sales'),
// 'class' => "pull-left",
'url' => Yii::$app->homeUrl,
'linkOptions' => ['id' => 'sales',
'class' => 'navbar-left',
],
];
// Show Admin content to manager+ users
if (Yii::$app->user->can('useAdminContent'))
{
$menuItems[] = ['label' => Yii::t('app', 'Administration'),
'url' => Yii::$app->homeUrl,
'linkOptions' => ['id' => 'admin'],
'options' => [
'class' => 'navbar-left',
],];
}
// display Logout to logged in users
if (!Yii::$app->user->isGuest) {
$menuItems[] =
[
'label' => Yii::t('app', 'Logout'). ' (' . Yii::$app->user-
>identity->username . ')',
'url' => ['/site/logout'],
'linkOptions' => ['data-method' => 'post']
//****** This is where I want the user image to be shown ****//
];
}
}
// echo navbar with selected items
echo Nav::widget([
'options' => ['class' => 'navbar-nav navbar-right'],
'items' => $menuItems,
]);
NavBar::end();
?>
<div class="container">
<?= Alert::widget() ?>
<?= $content ?>
</div>
endBody() ?>
Provided the user model has an attribute imageUrl it would be accessible through:
Yii::$app->user->identity->imageUrl
This would become something like:
If you would like to add it to the logout button:
$menuItems[] = [ 'encode'=>false, label' => Yii::t('app', 'Logout'). ' (' . Yii::$app->user- >identity->username . ') ' . Html::img(Yii::$app->user- >identity->imageUrl), 'url' => ['/site/logout'], 'linkOptions' => ['data-method' => 'post']];
make sure you include 'encode' => false,
To add it as a separate item (for instance, to give it a different link):
$menuItems[] = [ 'encode' => false, 'label' => Html::image(Yii::$app->user- >identity->imageUrl)];
Edit:
Make sure you include the Html with a use statement in your view, or provide the full namespace declaration with Html class.

Ajax Filtering with GridView in Yii

I want to use PJax in Yii Grid View, not with the associative filter that comes inside the Grid View, but with the Search Filter that's outside it, so it can filter the results inside.
Here is the source of the index file:
<div class="cars-index">
<h1><?= Html::encode($this->title) ?></h1>
<?php echo $this->render('_search', ['model' => $searchModel]); ?>
<p>
<?= Html::a('Create Cars', ['create'], ['class' => 'btn btn-success']) ?>
</p>
<?= GridView::widget([
'dataProvider' => $dataProvider,
'columns' => [
'id',
'name',
['attribute' => 'code',
'label' => 'Colour',
'format' => 'raw',
'value' => 'colour',
'contentOptions' => function ($model, $key, $index, $column){
return ['style' => ' text-align: center; width: 100px;color:white;background-color:#'. $model -> code];
},
'headerOptions' => ['style' => 'text-align: center;'],
],
'price',
['class' => 'yii\grid\ActionColumn'],
],
]); ?>
Am I supposed to create an active form just for the part I want to filter? Or is there another way?
If You can't simply add the filter to you table like this
<?= GridView::widget([
'dataProvider' => $dataProvider,
'filterModel' => $searchModel,
I suggest you use a proper action and a proper search function called by submit based on a specified active form
for action form eg:
<?php $form = ActiveForm::begin([
'id' => 'search-form',
'method' => 'post',
'action' => ['controller/yourAction']
]); ?>
in the controller
$model = new yourActiveForm();
if ($model->load(Yii::$app->request->post()) ) {
$dataProvider = $searchModel->search( [ 'yuorSearchModel'=> ['your_att1' => $model->yourValue1]]);
}
then your render
Conforming to yii2 doc
Pjax only deals with the content enclosed between its begin() and
end() calls, called the body content of the widget. By default, any
link click or form submission (for those forms with data-pjax
attribute) within the body content will trigger an AJAX request. In
responding to the AJAX request, Pjax will send the updated body
content (based on the AJAX request) to the client which will replace
the old content with the new one. The browser's URL will then be
updated using pushState. The whole process requires no reloading of
the layout or resources (js, css).
You may configure $linkSelector to specify which links should trigger
pjax, and configure $formSelector to specify which form submission may
trigger pjax.
You must add the
<?php Pjax::begin(); ?>
.... your active form
<?php Pjax::end(); ?>
and configure the proper $linkSelect and $formSelector
In your filter view:
<div id="myFilter">
<?php $form = ActiveForm::begin([
'id' => 'myFilterForm',
'method' => 'post',
'action' => [...],
]); ?>
...
</div>
And make sure you render filter between Pjax::begin and Pjax::end
Yet here comes the trick. If your server does not respond within default timeout, Pjax gets ignored and page reloaded, so make sure the timeout is big enough:
<?php Pjax::begin([
'id'=>'myGrid',
'timeout' => 10000, // <------------ THIS !!!!!!!
'formSelector' => '#myFilterForm'
]); ?>
<?= $this->render('myFilter', ['model' => $searchModel]); ?>
<?= GridView::widget([
...
]); ?>
<?php Pjax::end(); ?>
Also in your controller, you might want to "reset" the search model, so only data from the request used are attributes actually used by search:
public function actionSearch()
{
$searchModel = new MySearch();
if ($searchModel->load(Yii::$app->request->post())) {
$searchModel = new MySearch(); // "reset"
$dataProvider = $searchModel->search(Yii::$app->request->post());
} else {
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
}
return $this->render('search', [
'searchModel' => $searchModel,
'dataProvider' => $dataProvider,
]);
}
Hope this helps. Cheers!

CgridView ajax pagination

in my project i am using the cgridview inside a modal window. now i am facing the problem that when i select the pagination the page get reloaded. how can i load the grid view with ajax pagination. my ui code id given below...
<div id="pick-category-modal" title="Pick Category" class="popupinfoholder2">
<div style="width:700px; height:auto;">
<?php
$widget = $this->widget('zii.widgets.grid.CGridView', array(
'id' => 'response-grid',
'dataProvider' => $pickdataset->pickSection(),
'cssFile' => Yii::app()->baseUrl . '/media/css/gridview.css',
'summaryText' => '',
'ajaxUpdate'=>true,
'enablePagination' => true,
'template' => '{items}',
'pager' => array(
'class' => 'LinkPager',
'cssFile' => false,
'header' => false,
'firstPageLabel' => 'First',
'prevPageLabel' => 'Previous',
'nextPageLabel' => 'Next',
'lastPageLabel' => 'Last',
),
'columns' => array(
.....................
.....................
),));
?>
<div class="grid-view-footer">
<div class="paginationholder">
<div id="pagination">
<?php $widget->renderPager(); ?>
</div>
</div>
</div>
</div>
</div>
Just testing this myself a moment ago, it appears that the pager kicked out with renderPager() does not work with the AJAX setting. The CGridView JavaScript only binds the AJAX to pagers inside the <div> rendered by the widget (#response-grid here), so it does not find and use your additional pager.
I just use the default pager that the CGridView renders at the bottom, and that works fine.
You could override the CGridView JS to fix this, or list it as an enhancement on the Yii bug tracker: http://code.google.com/p/yii/issues/list

Resources