I am writing my own Auth component for CakePHP 2, which inherit from BaseAuthenticate. This component use an external lib (located in /usr/share/php) which save information in the $_SESSION variable.
My problem is that when I change of page, all this information is removed from $_SESSION, so the external lib do not sees that I am already connected.
I tried to save the content of $_SESSION added by the lib by using $this->Session->write in Auth.MyAuthenticateComponent, but this is also removed.
Thanks for your help.
edit :
The code :
class AppController extends Controller {
public $use = array('User');
public $components = array(
'Session',
'Auth' => array(
'loginRedirect' => array('controller' => 'Sheets', 'action' => 'index'),
'logoutRedirect' => array('controller' => 'Users', 'action' => 'login')
)
);
public function beforeFilter()
{
$this->Auth->authenticate = array('Arise');
}
}
class UsersController extends AppController
{
public $helpers = array('Html', 'Form');
public function beforeFilter()
{
parent::beforeFilter();
$this->Auth->allow('login');
$this->Auth->authenticate = array('Arise');
}
public function login()
{
if ($this->request->is('post')) {
if ($this->Auth->login())
return $this->redirect($this->Auth->redirect());
else
$this->Session->setFlash('Error');
}
}
public function logout()
{
$this->redirect($this->Auth->logout());
}
}
App::uses('BaseAuthenticate', 'Controller/Component/Auth');
require_once('/usr/share/php/openid/consumer/consumer.php');
class AriseAuthenticate extends BaseAuthenticate
{
protected function _ariseAuthenticate($openid_url)
{
$consumer =& AriseOpenID::getInstance();
$required = array(
'http://somewhere/types/identifiant',
'http://axschema.org/namePerson/first',
'http://axschema.org/namePerson/friendly'
);
$consumer->setReturnTo('http://mysite/users/login');
$consumer->setTrustRoot('http://mysite/users/login');
$consumer->authenticate($openid_url, $required);
if ($consumer->isLogged()) {
$first_name = $consumer->getSingle('http://axschema.org/namePerson/first');
$nick = $consumer->getSingle('http://axschema.org/namePerson/friendly');
$id_arise = $consumer->getSingle('http://openid.iiens.net/types/identifiant');
return array(
'id_arise' => $id_arise,
'first_name' => $first_name,
'nick' => $nick
);
}
return false;
}
public function checkUser($result)
{
$User = ClassRegistry::init('User');
$result = $User->find('first', array(
'conditions' => array(
'id_arise' => $result['id_arise']
)
));
if (!$result) {
$User->create();
$User->save(array(
'id_arise' => $result['id_arise'],
'first_name' => $result['first_name'],
'nick' => $result['nick']
));
$result = $User->find('first', array(
'conditions' => array(
'id_arise' => $result['id_arise']
)
));
}
$user = $result['User'];
unset($result['User']);
return array_merge($user, $result);
}
public function authenticate(CakeRequest $request, CakeResponse $response)
{
if (!$request->is('post'))
return false;
$openid_url = (array_key_exists('login', $request->data))
? $request->data['login']['openid_url']
: NULL;
$openid_url = ($openid_url == '') ? NULL : $openid_url;
if ($result = $this->_ariseAuthenticate($openid_url))
return $this->checkUser($result);
return false;
}
public function getUser()
{
if ($result = $this->_ariseAuthenticate(NULL))
return $this->checkUser($result);
return false;
}
}
Are you unit testing your auth adapter? It is hard to tell what is wrong but I'm more or less sure authenticate() does not return the array but always false? Because if authenticate() returns valid user data it would be written to the session.
See http://api.cakephp.org/2.3/source-class-AuthComponent.html#543, that calls http://api.cakephp.org/2.3/source-class-AuthComponent.html#690 which calls all configured auth adapters. If your adapter returns an user array it should work. So I assume your adapter fails to return the array.
Related
Recently I try to change the encoding of the password on CakePHP by directly adding a new file. Unfortunately I do not seem to connect, namely that her works perfectly with the class Simple in sha256
AppController
class AppController extends Controller {
public $components = array('Auth' => array(
'authenticate' => array(
'Form' => array(
'fields' => array('username' => 'email','password' => 'password'),
'passwordHasher' => array(
'className' => 'Custom'),
'scope' => array('active' => '1')
)
)
));
UsersController(login)
App::uses('CustomPasswordHasher', 'Controller/Component/Auth');
class UsersController extends AppController{
public function login()
{
$erreur = false;
if($this->request->is('post')) {
if(!empty($this->data)) {
if(!$this->Auth->user('id')) {
if($this->Auth->login()) {
echo 'ok';
} else {
$erreur = "Identifiant incorrect.";
}
} else {
$erreur = "Vous êtes déjà connecter.";
}
} else {
$erreur = "Veuillez saisir vos identifiants";
}
$this->set(compact('erreur'));
}
}
}
CustomPasswordHasher
App::uses('AbstractPasswordHasher', 'Controller/Component/Auth');
class CustomPasswordHasher extends AbstractPasswordHasher {
public function hash($password)
{
$before = substr(Configure::read('Security.salt'),0,43);
$after = substr(Configure::read('Security.salt'),43,42);
$chaine = $before.$password.$after;
return hash("sha256",$chaine);
}
public function check($password, $email, $hashType = null) {
$this->User = ClassRegistry::init('User');
$u = $this->User->find('first', array(
'fields' => array('user.password'),
'conditions' => array('user.email' => $email)
));
if($u) {
return $this->hash($password) == $u['User']['password'];
}
return false;
}
}
An idea where my problem might come?
Thank you !
Hey guys i have read and studied the kohana orm and auth modules. so i want to implement am admin section to my website. i get the error above and i have googled but can't seem to find the answer. am using Kohana 3.3.4
so a created a controller called admin:
<?php defined('SYSPATH') or die('No direct script access!');
class Controller_Admin extends Controller_Dev
{
public $template = 'login_template';
public function action_index()
{
if (Auth::instance()->logged_in()) {
$this->redirect->body('admin/dashboard', 302);
}
$this->redirect('admin/login');
}
//lets login user
public function action_login()
{
$view = new View('admin_login');
$this->template->title = "Log in";
if ($_POST) {
$user = ORM::factory('user');
$status = $user->login($_POST);
if ($status) {
$this->redirect('admin/dashboard', 302);
}
else {
$errors = $_POST->errors('admin/login');
}
}
// Display the login form
$this->template->content = $view;
}
//lets logout user
public function action_logout()
{
Auth::instance()->logout();
$this->redirect('admin/login', 302);
}
//lets register new users
public function action_register()
{
$view = View::factory('admin_register')
->set('values', $_POST)
->bind('errors', $errors);
$this->template->title = "Registration Page";
if ($_POST)
{
$user = ORM::factory('User');
// The ORM::values() method is a shortcut to assign many values at once
/* $external_values = array(
// The unhashed password is needed for comparing to the password_confirm field
'password' => Arr::get($_POST, 'password'),
// Add all external values
) + Arr::get($_POST, '_external', array());
$extra = Validation::factory($external_values)
->rule('confirm_password', 'matches', array(':validation', ':field', 'password')); */
try
{
//$test = $extra; //Arr::get($_POST, 'password');
//$view->test = $test;
$data = $this->request->post();
$user->register($data);
// Redirect the user to his page
$this->redirect('admin/login');
}
catch (ORM_Validation_Exception $e)
{
$errors = $e->errors('models');
}
}
$this->template->content = $view;
}
and i created a model called user to help me validate the new user account before save it to the database:
<?php defined('SYSPATH') or die('No direct access allowed.');
class Model_User extends Model_Auth_User {
//public $_table_name = 'users';
protected $_has_many = array(
'user_tokens' => array('model' => 'user_token'),
'roles' => array('model' => 'role', 'through', 'roles_users'),
// for facbook, google+, twitter and yahoo indentities
'user_identity' => array(),
);
protected $_ignored_columns = array('confirm_password');
public function rules()
{
return array(
'username' => array(
array('not_empty'),
array('min_length', array(':value', 4)),
array('max_length', array(':value', 32)),
array(array($this, 'username_available')),
),
'password' => array(
'not_empty' => NULL,
'min_length' => array(5),
'max_length' => array(42),
),
'password_confirm' => array(
'matches' => array('password'),
),
'email' => array(
'not_empty' => NULL,
'min_length' => array(4),
'max_length' => array(127),
'email' => NULL,
),
);
}
public function filters()
{
return array(
'password' => array(
array(array($this, 'hash_password')),
),
);
}
public function username_available($username)
{
// There are simpler ways to do this, but I will use ORM for the sake of the example
//return ORM::factory('Member', array('username' => $username))->loaded();
// Check if the username already exists in the database
return ! DB::select(array(DB::expr('COUNT(username)'), 'total'))
->from('users')
->where('username', '=', $username)
->execute()
->get('total');
}
public function hash_password($password)
{
// Do something to hash the password
}
public function register($array)
{
$this->values($array);
$this->save();
// Create a new user record in the database
// Save the new user id to a cookie
cookie::set('user', $id);
return $id;
}
}
When i visit the admin registration page. it fails displaying an error which says:
ErrorException [ Warning ]: call_user_func_array() expects parameter 1 to be a valid callback, no array or string given
so please help me out because i think i might be missing something. Thanks in advance guys. Am using Kohana 3.3.4
I had the same error recently. You need to change line:
array(array($this, 'username_available')),
to line (for username):
array(array($this, 'unique'), array('username', ':value')),
as stated in https://kohanaframework.org/3.3/guide-api/Model_Auth_User#rules
I hope this helps you.
I'm looking for a solution to my problem. I'm trying create a webservice using CakePHP 2.
I created a CRUD and configured using AuthComponent to login. The AuthComponent was configured to use Form. When I try execute some function of controller to return a JSON doesn't works and show me code of page index.php
I think if I do configure Basic Auth works, but when I try add Basic Auth in $components it's opened access, all actions can be access on browser.
How could I configure Basic and Form of AuthComponent to works together ?
I'm trying this, but doesn't works, because all actions are opened to access
class AppController extends Controller {
public $components = array("RequestHandler", "Auth", "Session");
public function beforeFilter(){
$this->Auth->authenticate = array(
'Basic' => array('userModel' => 'User',
'fields'=> array(
'username' => 'email',
'password' => 'senha'
),
'scope' => array(
'User.status' => 1
)
),
'Form' => array('userModel' => 'User',
'fields'=> array(
'username' => 'email',
'password' => 'senha'
),
'scope' => array(
'User.status' => 1
)
),
);
$this->Auth->loginAction = array(
'controller' => 'users',
'action' => 'login'
);
$this->Auth->loginRedirect = array(
'controller' => 'matriculas',
'action' => 'index'
);
$this->Auth->logoutRedirect = array(
'controller' => 'users',
'action' => 'login'
);
$this->Auth->authorize = "Controller";
$this->Auth->authError = "Efetue login de acesso";
$this->Auth->allow("login");
}
public function isAuthorized($user) {
if (isset($user['role']) && $user['role'] === 'admin') {
return true; // Admin pode acessar todas actions
}
return false; // Os outros usuários não podem
}
}
UsersController
class UsersController extends AppController {
public $components = array('Paginator');
public function index() {
$this->User->recursive = 0;
$this->set('users', $this->Paginator->paginate());
}
public function add() {
if ($this->request->is('post')) {
$this->User->create();
if ($this->User->save($this->request->data)) {
$this->Session->setFlash(__('The user has been saved.'));
return $this->redirect(array('action' => 'index'));
} else {
$this->Session->setFlash(__('The user could not be saved. Please, try again.'));
}
}
}
public function edit($id = null) {
if (!$this->User->exists($id)) {
throw new NotFoundException(__('Invalid user'));
}
if ($this->request->is(array('post', 'put'))) {
if ($this->User->save($this->request->data)) {
$this->Session->setFlash(__('The user has been saved.'));
return $this->redirect(array('action' => 'index'));
} else {
$this->Session->setFlash(__('The user could not be saved. Please, try again.'));
}
} else {
$options = array('conditions' => array('User.' . $this->User->primaryKey => $id));
$this->request->data = $this->User->find('first', $options);
}
}
public function login(){
$this->layout = "layout";
if($this->request->is("post")){
if ($this->Auth->login()) {
$this->redirect($this->Auth->redirect());
}else{
$this->Session->setFlash(__('Usuário ou senha inválido'));
}
}
}
public function logout(){
$this->redirect($this->Auth->logout());
}
/********** WEB SERVICES FUNCTIONS *********/
/** return all users **/
public function findAll(){
$this->set("users", $this->User->find('all'));
$this->set(array(
"_serialize" => 'users',
));
}
/** add new user from app **/
public function addUserFromApp(){
$this->layout=null;
$data = $this->request->input("json_decode", true);
echo $data;
}
}
I am unable to get the Auth component to login once passed credentials in a post method.
I am using CakePHP2.*
I am trying to write a web service.
Please below the code i have written to configure the Auth component in the AppController and below that the UserController for the User model
class AppController extends Controller {
public $components = array(
'DebugKit.Toolbar',
'Session',
'Auth' => array(
'loginRedirect' => array('controller' => 'user', 'action' => 'view'),
'logoutRedirect' => array('controller' => 'user', 'action' => 'home'),
'authenticate' => array('Form' => array('fields' => array('username'=>'username','password'=>'password'))),
'userScope'=> array('User.active_yn' => 1),
'userModel'=>'User',
'loginAction'=>array('controller' => 'user', 'action' => 'login'),
'autoRedirect'=>true,
'authError'=>'You dont have access to that area. Please login first.',
'loginError'=>'Username or password entered is incorrect. Please try again.',
'authorize' => array('Controller') // Added this line
)
);
public function isAuthorized($user) {
// Admin can access every action
if (isset($user['active_yn']) && $user['active_yn'] === 1) { //admin
return true;
}
// Default deny
return false;
}
public function beforeFilter() { }
}
class UserController extends AppController {
public $helpers = array('Html', 'Form', 'Session');
public $components = array('Session');
public $validate = array(
'email' => array('rule' => 'notEmpty')
);
public function index() {
$this->set('User', $this->User->find('all'));
}
public function view($id = null) {
if (!$id) {
throw new NotFoundException(__('Invalid User'));
}
$User = $this->User->findById($id);
if (!$User) {
throw new NotFoundException(__('Invalid User'));
}
$this->set('User', $User);
}
public function login() {
if ($this->request->is('post')) {
if ($this->Auth->login()) {
return $this->redirect($this->Auth->redirect());
}
$this->Session->setFlash(__('Invalid username or password, try again'));
}
$this->set('request', $this->request->data);
}
public function logout() {
return $this->redirect($this->Auth->logout());
}
public function beforeFilter() {
parent::beforeFilter();
$this->Auth->allow('view','login','logout');
}
}
Your controller names in the Auth configuration need to be in plural
I thought I have to ask here some help to my problem. I've spend whole evening with this. I have a login method in UsersController like this:
public function login() {
if ( $this->request->is( 'post' ) ) {
if ( $this->Auth->login() ) {
$this->redirect( array( 'controller' => 'reservations', 'action' => 'index' ) );
} else {
$this->Session->setFlash( __( 'Login error.' ), 'flashError' );
}
}
}
I trying to test this with PHPUnit, so I can be sure that only valid users can login → after a successful login they will be redirected to a specific page. Here's my testLogin method in UsersControllerTest class:
function testLogin() {
$UsersController = $this->generate( 'Users', array(
'components' => array(
'Auth' => array( 'user' )
),
)
);
$UsersController->Auth->expects( $this->any() )
->method( 'user' )
->with( 'id' )
->will( $this->returnValue( 2 ) );
$data = array( 'User' => array(
'student_number' => 1111111,
'password' => 'qwerty'
) );
//$UsersController->Auth->login( $data['User'] );
$this->testAction( '/users/login', array( 'data' => $data, 'method' => 'get' ) );
$url = parse_url( $this->headers['Location'] );
$this->assertEquals( $url['path'], '/reservations' );
}
I am still learning the basics of unit testing with CakePHP. I get this error:
PHPUNIT_FRAMEWORK_ERROR_NOTICE
Undefined index: Location
Test case: UsersControllerTest(testLogin)
I have no idea what causes this... What's wrong with my test method and how it should be written?
Thanks!
I got this working with the following code:
function testLogin() {
//mock user
$this->Users = $this->generate( 'Users', array(
'components' => array(
'Security' => array( '_validatePost' ),
)
) );
//create user data array with valid info
$data = array();
$data['User']['student_number'] = 1234567;
$data['User']['password'] = '[valid password here]';
//test login action
$result = $this->testAction( "/users/login", array(
"method" => "post",
"return" => "contents",
"data" => $data
)
);
$foo[] = $this->view;
//debug($foo);
//test successful login
$this->assertNotNull( $this->headers['Location'] );
$this->assertContains( 'reservations', $this->headers['Location'] );
$this->assertNotContains( '"/users/login" id="UserLoginForm"', $foo );
//logout mocked user
$this->Users->Auth->logout();
}
I use this testcase to override the cake Auth call and Session and check if the login is successful.
this is more of a generic solution that i use in my testing., to get the values put into the session after the user logs in and also to check if the login is successful.
<?php
App::uses('UsersController', 'Controller');
App::uses('AuthComponent', 'Controller/Component');
App::uses('CakeRequest', 'Network');
App::uses('CakeResponse', 'Network');
$_SERVER['HTTP_USER_AGENT'] = '';
class stubSession {
public $data = array();
public function write($key, $value){
$this->data[$key] = $value;
}
public function read($key){
if(array_key_exists($key, $this->data)){
return $this->data[$key];
}
}
public function renew() {
}
public function setFlash(){
}
public function delete() {
}
public function check(){
}
}
class stubRequest {
public $data = array();
function __construct($data) {
$this->data = $data;
}
public function is() {
return true;
}
public function clientIp(){
}
}
class stubAuthComponent extends AuthComponent{
public static $_session;
public function __construct($args, $session){
parent::__construct($args);
$this->Session = $session;
self::$_session = $session;
$this->request = new CakeRequest();
$this->response = new CakeResponse();
}
public function loggedIn() {
return self::$_session->read(self::$sessionKey) != array();
}
public function logout(){
}
public static function user($key) {
$user = self::$_session->read(self::$sessionKey);
return $user[$key];
}
}
class UsersControllerTest extends UsersController {
function __construct($data){
$this->User = ClassRegistry::init('User');
$this->Session = new stubSession();
$this->Auth = new stubAuthComponent(new ComponentCollection(), $this->Session);
$this->request = new stubRequest($data);
}
public function redirect(){
}
}
class TestUsersController extends CakeTestCase {
public function testLogin(){
$_POST = array('User' => array('email' => 'mail#someemail.com', 'username' => 'mail#someemail.com', 'password' => 'abcd1234'));
$usersController = new UsersControllerTest($_POST);
$usersController->login();
$login = $usersController->Auth->loggedIn();
//debug($usersController->Session->data); //you can test the session key value in here
$this->assertEquals($login, true);
}
}