I have a CakePHP application running on Cake PHP 3.8.13 and CakeDC Users 8.5.1.
I am currently able to log on using the username field, but I would like to use the email field for authentication. I have followed the instructions on https://github.com/CakeDC/users/blob/master/Docs/Documentation/Configuration.md#using-the-users-email-to-login but the system is still trying to use the username field. If I change email to username in the src/Template/Plugin/CakeDC/Users/Users/login.ctp I can log in using the username.
How can I get it to use the email field instead?
src/Application.php
<?php
namespace App;
use Cake\Core\Configure;
use Cake\Core\Exception\MissingPluginException;
use Cake\Error\Middleware\ErrorHandlerMiddleware;
use Cake\Http\BaseApplication;
use Cake\Http\Middleware\SecurityHeadersMiddleware;
use Cake\Routing\Middleware\AssetMiddleware;
use Cake\Routing\Middleware\RoutingMiddleware;
use Cake\Http\Middleware\EncryptedCookieMiddleware;
class Application extends BaseApplication
{
/**
* {#inheritDoc}
*/
public function bootstrap()
{
// Call parent to load bootstrap from files.
parent::bootstrap();
$this->addPlugin('AuditStash');
if (PHP_SAPI === 'cli') {
try {
$this->addPlugin('Bake');
} catch (MissingPluginException $e) {
// Do not halt if the plugin is missing
}
$this->addPlugin('Migrations');
}
/*
* Only try to load DebugKit in development mode
* Debug Kit should not be installed on a production system
*/
if (Configure::read('debug')) {
$this->addPlugin(\DebugKit\Plugin::class);
}
$this->addPlugin(\CakeDC\Users\Plugin::class);
Configure::write('Users.config', ['users']);
$identifiers = Configure::read('Auth.Identifiers');
$identifiers['Authentication.Password']['fields']['username'] = 'email';
Configure::write('Auth.Identifiers', $identifiers);
}
/**
* Setup the middleware queue your application will use.
*
* #param \Cake\Http\MiddlewareQueue $middlewareQueue The middleware queue to setup.
* #return \Cake\Http\MiddlewareQueue The updated middleware queue.
*/
public function middleware($middlewareQueue)
{
$securityHeaders = new SecurityHeadersMiddleware();
$securityHeaders
->setCrossDomainPolicy()
->setReferrerPolicy()
->setXFrameOptions()
->setXssProtection()
->noOpen()
->noSniff();
$middlewareQueue
// Add security headers
->add($securityHeaders)
// Catch any exceptions in the lower layers,
// and make an error page/response
->add(ErrorHandlerMiddleware::class)
// Handle plugin/theme assets like CakePHP normally does.
->add(new AssetMiddleware([
'cacheTime' => Configure::read('Asset.cacheTime')
]))
// Add routing middleware.
// Routes collection cache enabled by default, to disable route caching
// pass null as cacheConfig, example: `new RoutingMiddleware($this)`
// you might want to disable this cache in case your routing is extremely simple
->add(new RoutingMiddleware($this, '_cake_routes_'));
$cookies = new EncryptedCookieMiddleware(
// Names of cookies to protect
['remember_me', 'csrfToken'],
Configure::read('Security.cookieKey')
);
$middlewareQueue->add($cookies);
return $middlewareQueue;
}
}
src/config/users.php
<?php
return [
// Table used to manage users
'table' => 'Users',
// Controller used to manage users plugin features & actions
'controller' => 'Users',
// configure Auth component
'auth' => true,
// Password Hasher
'passwordHasher' => '\Cake\Auth\DefaultPasswordHasher',
// token expiration, 1 hour
'Token' => ['expiration' => 3600],
'Email' => [
// determines if the user should include email
'required' => true,
// determines if registration workflow includes email validation
'validate' => true,
],
'Registration' => [
// determines if the register is enabled
'active' => false,
// determines if the reCaptcha is enabled for registration
'reCaptcha' => true,
// allow a logged in user to access the registration form
'allowLoggedIn' => false,
//ensure user is active (confirmed email) to reset his password
'ensureActive' => false,
// default role name used in registration
'defaultRole' => 'user',
],
'reCaptcha' => [
// reCaptcha key goes here
'key' => null,
// reCaptcha secret
'secret' => null,
// use reCaptcha in registration
'registration' => false,
// use reCaptcha in login, valid values are false, true
'login' => false,
],
'Tos' => [
// determines if the user should include tos accepted
'required' => true,
],
'Social' => [
// enable social login
'login' => false,
// enable social login
'authenticator' => 'CakeDC/Users.Social',
],
'GoogleAuthenticator' => [
// enable Google Authenticator
'login' => false,
'issuer' => null,
// The number of digits the resulting codes will be
'digits' => 6,
// The number of seconds a code will be valid
'period' => 30,
// The algorithm used
'algorithm' => 'sha1',
// QR-code provider (more on this later)
'qrcodeprovider' => null,
// Random Number Generator provider (more on this later)
'rngprovider' => null
],
'Profile' => [
// Allow view other users profiles
'viewOthers' => false,
'route' => ['plugin' => 'CakeDC/Users', 'controller' => '\Users', 'action' => 'profile'],
],
'Key' => [
'Session' => [
// session key to store the social auth data
'social' => 'Users.social',
// userId key used in reset password workflow
'resetPasswordUserId' => 'Users.resetPasswordUserId',
],
// form key to store the social auth data
'Form' => [
'social' => 'social'
],
'Data' => [
// data key to store the users email
'email' => 'email',
// data key to store email coming from social networks
'socialEmail' => 'info.email',
// data key to check if the remember me option is enabled
'rememberMe' => 'remember_me',
],
],
// Avatar placeholder
'Avatar' => ['placeholder' => 'CakeDC/Users.avatar_placeholder.png'],
'RememberMe' => [
// configure Remember Me component
'active' => false,
'checked' => true,
'Cookie' => [
'name' => 'remember_me',
'Config' => [
'expires' => '1 month',
'httpOnly' => true,
]
]
],
];
src/Template/Plugin/CakeDC/Users/Users/login.ctp
<?php
use Cake\Core\Configure;
?>
<div class="users form">
<?= $this->Flash->render('auth') ?>
<?= $this->Form->create() ?>
<fieldset>
<legend><?= __d('CakeDC/Users', 'Please enter your username and password') ?></legend>
<?= $this->Form->control('email', ['required' => true]) ?>
<?= $this->Form->control('password', ['required' => true]) ?>
</fieldset>
<?= $this->Form->button(__d('CakeDC/Users', 'Login')); ?>
<?= $this->Form->end() ?>
</div>
Add this code
$identifiers = Configure::read('Auth.Identifiers');
$identifiers['Authentication.Password']['fields']['username'] = 'email';
Configure::write('Auth.Identifiers', $identifiers);
to your pluginBootstrap() function in Application, or ensure the identifier configuration is correctly updated in the config/users.php file, so the plugin will be able to read it and configure Authentication internally.
Related
I'm developing an API for an application that I'm creating. This application will get all the information from this API (first will auth the user, then get information)
Right now, I'm trying to make the user send a username and password to the API, it validates the information and returns if it's "ok" or "not", very simple to start only. I know all the security involved in this, just need to get this working.
Already managed to send the username and passsword on the API Side (and i'm 100% sure that the data is correctly saved). Though, when I call
$this->Auth->identify($this->request->data);
It always returns false to me (already tried with parameters and without, result is the same).
I'm using the HttpRequester, firefox plugin, to send information.
I've did a debug of $this->request->data to see if the information is correct, and it is. Can't do a find on database since the password is being hashed.
The database password field is a varchar with 300 length (already tried with 255, also no work)
Thanks for the help
EDIT:
$this->loadComponent('Auth', [
'authenticate' => [
'Form' => [
'fields' => [
'username' => 'email',
'password' => 'password'
]
],
]
]);
if($this->request->is('POST')){
//debug($this->request->data);
$user = $this->Auth->identify($this->request->data);
}
Users Table:
protected $_accessible = [
'*' => true,
];
/**
* Hash password
*
*/
protected function _setPassword($password)
{
return (new DefaultPasswordHasher)->hash($password);
}
protected function _getFullName()
{
if(isset($this->_properties['full_name'])){
return ucwords(mb_strtolower($this->_properties['full_name']));
}
}
ps: Also tried doing the following (replacing the variables form post, but also no luck)
$this->request->data['username'] = "xxxx";
$this->request->data['password'] = "zzzz";
Problem is here
'Form' => [
'fields' => [
'username' => 'email', //email is your database field
'password' => 'password' // password is your database field name
]
],
Your code should be
'Form' => [
'fields' => [
'username' => 'username',
'password' => 'password'
]
],
Details check Configuring Authentication Handlers
I'm using Yii2's DBSession class to store web application sessions into a database table, called session.
This table by default only has 3 columns - id, expire and data.
I'd like to store additional information into this table, like the user_id of a logged in user.
Edit: So there's a parent class called yii\web\MultiFieldSession but no examples about how it's used. I'll see what I can discover...
create migration:
$this->createTable('session', [
'id' => $this->char(40)->notNull(),
'expire' => $this->integer(),
'data' => $this->binary(),
'user_id' => $this->integer()
]);
$this->addPrimaryKey('session_pk', 'session', 'id');
add this to config:
'components' => [
'session' => [
'class' => 'yii\web\DbSession',
'writeCallback' => function($session){
return [
'user_id' => Yii::$app->user->id
];
}
// 'db' => 'mydb', // the application component ID of the DB connection. Defaults to 'db'.
// 'sessionTable' => 'my_session', // session table name. Defaults to 'session'.
],
Check writeCallBack (a callback that will be called during session data writing)
By Using callback you can set your own database fields.. Used by composeField()
I didn't want a function in a config file, so I ended up with this:
config/web.php
'components' =>
[
'session' =>
[
'class' => 'app\models\Session',
],
],
models/Session.php
class Session extends \yii\web\DbSession
{
protected function composeFields($id = null, $data = null)
{
$fields = parent::composeFields($id, $data);
$fields['user_id'] = \Yii::$app->user->id;
return $fields;
}
}
I want send email by console with Swift_SmtpTransport.
The same transport settings work in common/config/main-local.php and don't work in console/config/main-local.php.
In console in config/main-local.php I have:
<?php
return [
'components' => [
'mail' => [
'class' => 'yii\swiftmailer\Mailer',
'viewPath' => '#common/mail',
'htmlLayout' => '#common/mail/layouts/html',
'textLayout' => '#common/mail/layouts/text', // custome layout
'transport' => [
'class' => 'Swift_SmtpTransport',
'host' => 'gator.hostgator.com',
'username' => 'test#pix.com',
'password' => '*******',
'port' => '465',
'encryption' => 'ssl',
],
],
],
];
With this configuration (and in common the settings are the same and work) I load the script by command and no email send and no error.
With this (I delete the transport settings) I run the same script by command and the email send ok:
<?php
return [
'components' => [
'mail' => [
'class' => 'yii\swiftmailer\Mailer',
'viewPath' => '#common/mail',
'htmlLayout' => '#common/mail/layouts/html',
'textLayout' => '#common/mail/layouts/text', // custome layout
],
],
];
In console/controllers/CronController I have this:
<?php
namespace console\controllers;
use yii\console\Controller;
use backend\models\Definicoes;
use common\models\Acordo;
use Yii;
/**
* Cron controller
*/
class CronController extends Controller {
public function actionIndex() {
$data_hoje = date('Y-m-d');
$model_definicoes = Definicoes::find()->one();
Yii::$app->mail->compose('#common/mail/cron_acordo', ['model_definicoes' => $model_definicoes])
->setFrom([Yii::$app->params['adminEmail'] => Yii::$app->params['nome']])
->setSubject('Alert')
->setTo('ideinto#gmail.com')
->send();
}
}
Why this happen? I canĀ“t use transport in console?
Thanks!
another way to check if your transport settings are correct
you could set the transport directly in CronController.php
\Yii::$app->mail->setTransport( [
'class' => 'Swift_SmtpTransport',
'host' => 'gator.hostgator.com',
'username' => 'test#pix.com',
'password' => '*******',
'port' => '465',
'encryption' => 'ssl',
]);
before this line
Yii::$app->mail->compose('#common/mail/cron_acordo', ['model_definicoes' => $model_definicoes])
This is because yii merge config in common/main.php with yours at console/main.php.
Reset your config to send directly:
'mailer' => [
'useFileTransport' => false,
//other configs
]
See file: yii\mail\BaseMailer.php, have this comment at line 77-78:
/**
* #var boolean whether to save email messages as files under [[fileTransportPath]] instead of sending them
* to the actual recipients. This is usually used during development for debugging purpose.
* #see fileTransportPath
*/
public $useFileTransport = false;
You should make sure your configurations in console.php are right, make sure the key mailer is configured.
Good luck !
So, I came across this when I was looking for an answer to a problem I had. I was able to find the answer to my problem by enabling the logging for the SwiftMailer which ended up finding "Sender address rejected: User unknown in relay recipient table." Essentially, the mail that I was using in the "from" address was different in the console portion than it was from the frontend portion, and that was the issue, it didn't even have to do with the config.
I was only able to figure this out by looking at the logging, so to enable logging, in your Yii configuration under the 'mail' component (or whatever you're calling it) add the key=>value pair 'enableSwiftMailerLogging' => true (example of where is in this question: Config mailer parameters from model - Yii2). Then, in your log component config, under the 'targets' you need to add
[
'class' => 'yii\log\FileTarget',
'categories' => ['yii\swiftmailer\Logger::add'],
]
It's slightly documented here: http://www.yiiframework.com/doc-2.0/yii-swiftmailer-logger.html
By doing this I was able to look into the logs (console/runtime/app.log for me) and find out why it wasn't sending out correctly from the console but was from the other areas of my app.
I have 2 different tables user and organiser and i am trying to create 2 different login for both users.
I am able to sign them up easily and get the record in database but after saving the record i get the error on following code line
if ($user = $model->signup()) {
if (Yii::$app->getUser()->login($user)) { //yii\web\IdentityInterface error
return $this->goHome();
}
}
Following is my configuration module
'components' => [
'user' => [
'identityClass' => 'common\models\User',
'enableAutoLogin' => true,
'identityCookie' => [
'name' => '_frontendOrganiser', // unique for frontend
],
],
'users' => [
'class' => 'yii\web\User',
'identityClass' => 'common\models\Users',
'enableAutoLogin' => false,
'enableSession' => true,
'identityCookie' => [
'name' => '_frontendUser', // unique for frontend
],
],
'session' => [
'name' => 'PHPFRONTSESSID',
'savePath' => sys_get_temp_dir(),
],
]
So what is wrong am i doing here? Do i need to create any other Interface or something or provide different interface for different module?
And i had to do this because organiser table uses password_hash technique to log in the users where my user table is from another project and they uses md5 technique, so i had to create separate module for both users.
Argument 1 passed to yii\web\User::login() must be an instance of yii\web\IdentityInterface, instance of common\models\Users given, called in C:\xampp\htdocs\project\frontend\controllers\SiteController.php on line 202 and defined
The exact error statement is as above.
I think your user model don't implement the Identity interface correctly.
Try check you data model also (in your DB) this must contain all the field managed bay the interface.
And be sure you User implement the Identity Interface correctly.
and mapping the interface method with your model correctly..
See the interface doc for this http://www.yiiframework.com/doc-2.0/yii-web-identityinterface.html
I have configured an identification provider (IdP) part of a SSO system, using SimpleSAMLphp.
The main sections of my configuration files:
config/config.php
$config = array(
[...]
'enable.saml20-idp' => true,
'enable.shib13-idp' => true,
[...]
);
config/authsources.php
$config = array(
[...]
'*-sql' => array(
'sqlauth:SQL',
'dsn' => 'mysql:host=*.*.*.*;port=*;dbname=*',
'username' => '*',
'password' => '*',
'query' => 'SELECT *
FROM users
WHERE username = *
AND password = *',
),
[...]
);
metadata/saml20-idp-hosted.php
$metadata['__DYNAMIC:1__'] = array(
'host' => '__DEFAULT__',
'privatekey' => '../cert/*.key',
'certificate' => '../cert/*.pem',
'auth' => '*-sql',
'NameIDFormat' => 'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent',
'authproc' => array(
3 => array(
'class' => 'saml:AttributeNameID',
'attribute' => 'uid',
'Format' => 'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent',
),
),
);
metadata/saml20-idp-remote.php
$metadata['https://www.video2brain.com/shibboleth'] = array(
'AssertionConsumerService' => 'http://*/Shibboleth.sso/SAML2/POST',
'SingleSignOnService' => 'http://*/Shibboleth.sso/SAML2/POST',
'SingleLogoutService' => 'http://*/Shibboleth.sso/SLO/POST',
);
The certificates and metadata were successfully configurated. The SSO works fine.
But the service provider (SP) has requested that the IdP has to pass more info of the logged user. The authentication is passed when the query returns a row, but i can't access to the fields in the SELECT.
Currently, the final POST request that my IdP sent to their SP has the following parameters:
HTTP_SHIB_IDENTITY_PROVIDER=https://*/metadata.php,
HTTP_SHIB_AUTHENTICATION_INSTANT=2015-10-20T09:04:42Z,
HTTP_SHIB_AUTHENTICATION_METHOD=urn:oasis:names:tc:SAML:2.0:ac:classes:Password,
HTTP_SHIB_AUTHNCONTEXT_CLASS=urn:oasis:names:tc:SAML:2.0:ac:classes:Password,
HTTP_EMAIL=*#*.*,
HTTP_PERSISTENT_ID=!https://*/shibboleth-sp!6faa919dda0e40e5e42088bcd9beb639ed4dfa5e
And they want the full name of the user in a new parameter. Something like that:
[...]
HTTP_USER_NAME=FooUserName
I have tried using the "Adding attributes (core:AttributeAdd)" method but doesn't work. Is possible do that? Any doc, resource or example for this will be helpful.
Thanks.
I set the parameter as "givenName" instead of "name", and it works!
In the auth query, I put an alias for the user "name" as "givenName".
In the idp-hosted, in the "authproc" key I used de AttributeMap method to add the "givenName".
I did these things before, but I was trying to use "name" as the final parameter "name", and didn't work until I use "givenName".
Someone could say me why?
The parameter name is no configurable?
May be the SP and the IdP has to configure the same name in both sides?