CakePHP 3 Ldap authentication issue and clarification - php

I am working on integrating LDAP authentication in my project. and I followed the tutorial from official CakePHP site that guides through how to create a custom object in application src path and using those custom objects in AuthController.
So I created a folder called Auth in src with the file name called LdapAuthorize.php. The path looks like this src/Auth/LdapAuthorize.php
Here is my LdapAuthorize.php code:
namespace App\Auth;
use Cake\Auth\BaseAuthorize;
use Cake\Network\Request;
class LdapAuthorize extends BaseAuthorize {
public function authorize($user, Request $request) {
if ($user == 'username') { // where username is logged on ldap user on a computer.
return true;
}
}
}
I also called the object in AppController.php file. Here is my code:
public function initialize()
{
parent::initialize();
$this->loadComponent('Flash');
$this->loadComponent('Auth', [
'loginRedirect' => [
'controller' => 'Customers',
'action' => 'index'
],
'logoutRedirect' => [
'controller' => 'Pages',
'action' => 'display',
'home'
]
]);
$this->Auth->config('authenticate', [
'Ldap'
]);
}
So when I access the url http://localhost/AppPath/Dashboard/index I get Authentication adapter "Ldap" was not found.
Since this is my first experience with CakePHP, I couldn't find that many solution online that help troubleshoot any issues.
Adding additional code for LdapAuthenticate.php:
namespace App\Auth;
use Cake\Auth\BaseAuthenticate;
use Cake\Network\Request;
use Cake\Network\Response;
class OpenidAuthenticate extends BaseAuthenticate
{
public function authenticate(Request $request, Response $response)
{
$users = ["john", "ray"];
return $users;
}
}

What you need is a custom authentication adapter, your LdapAuthorize is a custom authorize adapter:
// in src/Auth/LdapAuthenticate.php
namespace App\Auth;
use Cake\Auth\BaseAuthenticate;
use Cake\Network\Request;
use Cake\Network\Response;
class LdapAuthenticate extends BaseAuthenticate {
protected $_host = 'your_ldap_server' ;
public function authenticate(Request $request, Response $response) {
$username = $request->data['username'] ;
$password = $request->data['password'] ;
$ds = #ldap_connect($this->_host) ;
if (!$ds) {
throw \Cake\Error\FatalErrorException ('Unable to connect to LDAP host.') ;
}
$basedn = "your ldap query... "
$dn = "uid=$username, ".$basedn;
$ldapbind = #ldap_bind($ds, $dn, $password);
if (!$ldapbind) {
return false ;
}
// Do whatever you want with your LDAP connection...
$entry = ldap_first_entry ($ldapbind) ;
$attrs = ldap_get_attributes ($ldapbind, $entry) ;
$user = [] ;
// Loop
for ($i = 0 ; $i < $attrs["count"] ; $i++) {
$user[$attrs[$i]] = ldap_values ($ldapbind, $entry, $attrs[$i])[0] ;
}
// Then close it and return the authenticated user
ldap_unbind ($ldapbind) ;
ldap_close ($ldapbind);
return $user ;
}
}

I was still having the same error after creating the custom authentication adapter suggested above.
I solved it changing
namespace App\Auth;
for
namespace Cake\Auth;
In LdapAuthenticate.php

Related

Listeners and Cookies in Laravel

I try to use setcookie in Laravel listeners with queue, setcookie return true but the problem is in chrome (Or Firefox) DevTools> Application> Cookies cookies are not set and not working.
My Class:
class FlarumEventSubscriber implements ShouldQueue
{
public function onUserLogin($event)
{
$user = $event->user;
$response = $this->authenticate($user->mobile, $user->password);
$token = $response['token'] ?: '';
setcookie('flarum_session', $token, time() + 99999999 , '/', 'localhost'); // ======> The problem
}
public function subscribe($events)
{
$events->listen(
'Illuminate\Auth\Events\Login',
'App\Listeners\FlarumEventSubscriber#onUserLogin'
);
}
private function authenticate($id, $password)
{
$endpoint = '/api/token';
$method = 'POST';
$data = [
'identification' => $id,
'password' => $password,
'lifetime' => 99999999
];
return $this->sendRequest($endpoint, $method, $data);
}
}
EventServiceProvider :
class EventServiceProvider extends ServiceProvider
{
/**flarum subscriber */
protected $subscribe = [
'App\Listeners\FlarumEventSubscriber',
];
}
In FlarumEventSubscriber class, I send a request after the user login to Laravel, and after that, some information needs to be stored in the cookie.
My efforts:
Use Cookie::queue() instead of setcookie() => Not working
Test on other domains => Not working
Info:
PHP v7.4.26
Laravel 8.0
Wampserver v3.2.6 64bit

Symfony 3.3 Web Service Login form

We are working in a login form, using simfony and a REST Webservice.
We have been searching in this link (http://symfony.com/doc/current/security/custom_provider.html)
The goal is login in with the form and the REST web service, updating my session data, like, name, doc, email, etc.
And with this data allow or deny the access to some pages or functions.
When we submit the form, we donĀ“t know how to use the data returned by the webservice, also if there are response or not.
This is our code:
SecurityController.php
<?php
namespace AppBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
class SecurityController extends Controller {
// public function loginAction(AuthenticationUtils $authenticationUtils) {
public function loginAction(Request $request, AuthenticationUtils $authenticationUtils) {
// $authenticationUtils = $this->get('security.authentication_utils');
$error = $authenticationUtils->getLastAuthenticationError();
$lastUsername = $authenticationUtils->getLastUsername();
return $this->render('AppBundle:Security:login.html.twig', array('last_username' => $lastUsername, 'error' => $error));
// return $this->render('AppBundle:Security:login.html.twig');
}
public function loginCheckAction() {
$ca = $this->get('webservice_user_provider');
print_r($ca);
exit;
}
}
Login.html.twig-----
<form class="form-signin" action="{{ path('app_user_login_check') }}" method="POST">
Security.yml-----------------------
webservice:
id: webservice_user_provider
Archivo services.yml----------------------------
webservice_user_provider:
class: AppBundle\Security\User\WebserviceUserProvider
WebserviceUserProvider.php-----------------------------
<?php
// src/AppBundle/Security/User/WebserviceUserProvider.php
namespace AppBundle\Security\User;
use AppBundle\Security\User\WebserviceUser;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
use Unirest;
class WebserviceUserProvider implements UserProviderInterface {
protected $user;
public function __contsruct(UserInterface $user) {
$this->user = $user;
}
public function loadUserByUsername($username) {
// make a call to your webservice here
print_r("Estoy en el controlador de usuario");
exit;
$headers = array('Accept' => 'application/json');
$password = $this->request->get('password');
$query = array('user' => $username, 'password' => _password);
$userData = Unirest\Request::post('http://127.0.0.10:8888/login', $headers, $query);
// pretend it returns an array on success, false if there is no user
if ($userData) {
$datos = $userData->raw_body;
// print_r($userData);
// print_r($userData->body);
// print_r($userData->raw_body);
$username = $datos['ldap']['document'];
$password = $datos['ldap']['document'];
$salt = $datos['ldap']['document'];
$roles = $datos['ldap']['document'];
$doc = $datos['ldap']['document'];
$full_name = $datos['ldap']['document'];
$userLdap = $datos['ldap']['document'];
$userEpersonal = $datos['ldap']['document'];
$mail = $datos['ldap']['document'];
$position = $datos['ldap']['document'];
return new WebserviceUser($username, $password, $salt, $roles, $documento, $full_name, $userLdap, $userEpersonal, $mail, $position);
}
throw new UsernameNotFoundException(
sprintf('Username "%s" does not exist.', $username)
);
}
public function refreshUser(UserInterface $user) {
if (!$user instanceof WebserviceUser) {
throw new UnsupportedUserException(
sprintf('Instances of "%s" are not supported.', get_class($user))
);
}
return $this->loadUserByUsername($user->getUsername());
}
public function supportsClass($class) {
return WebserviceUser::class === $class;
}
}
I will give you a general overview, for implementation details you may want to ask another question.
A REST web service should be stateless, i.e. (to simplify a bit) should have no session. To implement ACL you may have different strategies.
The easiest one is to perform authentication on each request. You may use http authentication, or you can use an API key as many webservices do. Your webservice will always authenticate the user as the first step in each request.
A slightly more secure strategy is to have authentication return you a temporary token. I.e. first you request the login action with whatever authentication system you choose (you can even have more than one) and you get back a randomly generated token associated with your user. In the next requests you include this token and the system know who you are.

CakePHP and REST Api for ionic (angular) app

Hello I try to setup cakephp for rest client (with login auth) for ionic (angular) app.
Ok, I configure CakePhp like this setup tutorial and for example I get data that:
public function projects()
{
$projects = $this->Projects->find('all');
$this->set([
'projects' => $projects,
'_serialize' => ['projects']
]);
}
and get data via $.http in Ionic
This work perfectly but I try to configure cake auth for mobile client.
I don't know how I do this. In my Resttest Controller i wrote code where set session Id for ionic app, but ionic not cache this session and I think is my cakePhp code is wrong.
CakePHP controller:
<?php
namespace App\Controller;
use App\Controller\AppController;
use Cake\Controller\Component\RequestHandlerComponent;
// use Cake\View\Helper\SessionHelper;
class ResttestController extends AppController
{
public function initialize()
{
parent::initialize();
$this->loadComponent('RequestHandler');
$this->loadModel('Projects');
$this->loadModel('Task');
$this->loadModel('User');
$this->viewBuilder()->layout(false);
$this->response->header('Access-Control-Allow-Origin', '*');
$this->loadComponent('Auth', [
'loginAction' => [
'controller' => $this->name,
'action' => 'login',
// '_ext'=>'json'
],
'authorize'=>['Controller'],
]);
// Basic setup
$this->Auth->config('authorize', ['Controller']);
}
public function login(){
header('Access-Control-Allow-Headers: Content-Type, x-xsrf-token');
$this->response->header('Access-Control-Allow-Methods', '*');
if($this->request->is('post')){
$postdata = file_get_contents("php://input");
$d = json_decode($postdata);
if($this->Auth->user()){
$response =array("success"=>2,'msg'=>'logged After');
}
// $d = $this->request->data;
if(!$d->password || !$d->login){
$response = array("success"=>0,'msg'=>'n');
}
$u = $this->User->find()
->where(['email'=>$d->login])
->first();
if($u){
$salt = $u->salt;
$input_password = crypt($d->password, '$2y$12$' . $salt);
$password = $u->password;
if($password == $input_password){
$tok = self::getToken();
$u->token = $tok;
$out = $this->Auth->setUser($u);
$response = array("success"=>1,'msg'=>'logged', 'token'=>$tok, 'out'=>$out,'sadga'=>$this->Auth->identify,'asf'=>$this->Auth,'adsafsfq'=>$d,'$this->request'=>$this->request,'$this->response'=>$this->response,'apache_request_headers '=>apache_request_headers());
}else{
$response = array("success"=>0,'msg'=>'n');
}
}else{
$response = array("success"=>0,'msg'=>'n');
}
}else{
$response =array("success"=>0,'msg'=>'n');
}
$this->set([
'response' => $response,
'_serialize' => ['response']
]);
}
private function getToken(){
return crypt(sha1(md5(uniqid(rand(), true))));
}
public function testAuth(){
}
}
This code return session and user data but not work and I think is not good method for mobile auth. Do you have any idea for auth for cakephp ?
How I make my code more security ?
When we split application to backend api and frontend, we should consider backend as stateless application. This mean you can't use session for auth.
Instead you should implements auth/login and auth/register rest endpoints that will return some token for example JWT.
For cakephp2 you can easely find such library: https://github.com/t73biz/cakephp2-jwt-auth
Use this authenticator instead of Form when you configure Auth component.
From front end side pass token like it is described in the plugin.

Facebook login in laravel 5.2 can't hold the session after redirect

I am using Facebook PHP SDK to log my user.
I created a guard called login for this
Here is my config file of auth.php
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'token',
'provider' => 'users',
],
'admin'=>[
'driver'=>'session',
'provider'=>'adminusers',
],
'verify'=>[
'driver'=>'session',
'provider'=>'verify',
],
'login'=>[
'driver'=>'session',
'provider'=>'users'
]
],
to access Facebook api i created a class in App\services namespace called it Facebook
App\Services\Facbook.php
<?php
namespace App\Services;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Config;
use App\Extensions\Facebook\FacebookLaravelPersistentDataHandler;
use Facebook\Facebook as FB;
use App;
class Facebook{
protected $fb;
protected $helper;
protected $permission;
protected $log;
protected $canvashelper;
protected $persistentDataHandler;
function __construct()
{
$this->fb = new FB([
'app_id'=>Config::get('facebook.app_id'),
'app_secret'=>Config::get('facebook.app_secret'),
'default_graph_version' => Config::get('facebook.default_graph_version'),
'persistent_data_handler' => new FacebookLaravelPersistentDataHandler(),
]);
$this->helper = $this->fb->getRedirectLoginHelper();
$this->permission = Config::get('facebook.permission');
$this->log = new Logging(Config::get('facebook.logfile'),'Facebook Log');
$this->canvashelper = $this->fb->getCanvasHelper();
$this->persistentDataHandler = new FacebookLaravelPersistentDataHandler();
}
public function FBAuthUrl()
{
if($this->isFBAuth())
{
return $this->helper->getLogoutUrl($this->persistentDataHandler->get('facebook_access_token'),route('facebook.logout'));
}
else
{
return $this->helper->getLoginUrl(route('facebook.callback'),$this->permission);
}
}
public function LoginCallback()
{
$accessToken = $this->helper->getAccessToken();
if(isset($accessToken))
{
$this->persistentDataHandler->set('facebook_access_token',(string) $accessToken);
}
}
public function isFBAuth()
{
return $this->persistentDataHandler->has('facebook_access_token');
}
public function getFBUser()
{
if($this->isFBAuth())
{
$this->fb->setDefaultAccessToken($this->persistentDataHandler->get('facebook_access_token'));
/*,user_birthday,user_tagged_places*/
$response = $this->fb->get("/me?fields=id,name,first_name,last_name,age_range,link,gender,locale,picture,timezone,updated_time,verified,email");
return $response->getGraphUser();
}
else
{
return false;
}
}
public function logout()
{
$this->persistentDataHandler->delete('facebook_access_token');
$this->persistentDataHandler->delete('state');
}
}
And Here is my UserController Where i write my login logic
class UserController extends Controller
{
.....
/*
* Facebook login callback function
* #param Object App\services\Facebook
* return redirect
*/
public function fbLogin(Facebook $facebook)
{
$facebook->LoginCallback();
/*
* get the usergraphnode from facebook
*/
$fbUser = $facebook->getFBUser();
/*
* Convert UserGraphNode User To Eloquent User
*/
$user = $this->getFBLoggedUser($fbUser);
/*
* Here Log the user in laravel System
*/
Auth::guard('login')->login($user);
//dump(Auth::guard($this->guard)->user());
dump(session()->all());
return reidrect('/');
}
public function getFBLoggedUser($fbUser)
{
if(User::where('email','=',$fbUser->getField('email'))->count())
{
$user = User::where('email','=',$fbUser->getField('email'))->first();
if($user->fb_app_id){
$user->fb_app_id = $fbUser->getField('id');
$user->save();
}
}
else
{
$user = $this->FBregister($fbUser);
}
return $user;
}
/**
* Register The user logged in from Facebook
*
* #param \Facebook\GraphNodes\GraphUser;
*
* return \App\Models\User
*/
public function FBregister($fbUser)
{
$user = new User();
$user->fname = $fbUser->getField('first_name');
$user->lname = $fbUser->getField('last_name');
$user->gender = $fbUser->getField('gender');
$user->email = $fbUser->getField('email');
$user->fb_app_id = $fbUser->getField('id');
$picture = $fbUser->getField('picture');
if($picture->isSilhouette()){
$user->profile_image = $picture->getUrl();
}
$user->save();
return $user;
}
.........
}
On Successful Facebook login redirect i am calling UserController#fbLogin
after calling Auth::guard()->login() i dump session it successfully show a login_login_randomstring=>UserId i session . but When i redirect it all session data lost.
But the weird thing is that it only happen when it calling through facebook redirect. If i use this function like normal login routes it works perfactaly like this
in route.php
Route::get('/login','UserController#login');
and in UserController
function login(){
$user = User::find(12);
Auth::guard('login')->login($user);
return redirect('/');
}
Using this method i can easily access Session data after redirecting from here but in facebook case it doesn't happening.
I stuck here for two days please anyone can help me
[Note: Please don't mention in your answer that i should grouped my routes in web middleware. ]
After digging very deep in laravel i finally found what i was doing wrong. And i am posting may be it help some in future.
Important thing :- Laravel save session very last in its request life-cycle. It saves session it sends header response. So if we echo something in controller class then it will send header response without doing saving session and our session will not save. In my case i use dump function in my controller which terminate the laravel default life-cycle and forcefully send header response to browser. that's why all of session data is lost. i remove dump() form my code and everything start working correctly
According to API documentation https://laravel.com/api/5.2/Illuminate/Auth/Guard.html you should call user() method to get the currently authenticated user. So i would suggest that instead of Auth::guard() use Auth::user($user).
try to use plugin socialite for login with facebook socialite
Facebook php sdk use $_SESSION.In laravel you cannot access this variable,laravel use personal class for session.
According to api code and your facebook documentation. Simple session working with request. You can save your data with
For put session in value
Session::put('userid','1');
Retrieve the value
$request->session()->get('userid') //or
{!! Session::get('userid') !!}
Very useful thing in your case.

Secure Ajax Requests made by YII

i'm currently writing a Application based on YII.
My action for index:
public function actionIndex() {
$data = array();
$data['server'] = Server::model()->findByPk(1);
$data['dataProvider'] = new CActiveDataProvider('ServerUserPermission', array('criteria' => array('condition' => 'serverID=:id', 'params' => array(':id' => 1))));
$this->render('index', $data);
}
my ajax action:
public function actionAddPermission($server) {
if(Util::checkServerPower($server, Permission::MODIFY_SERVER)) {
$perm = new ServerUserPermission;
$perm->userID = 1;
$perm->serverID = $server;
$perm->power = 10;
try {
if ($perm->save()) {
echo "OK";
} else {
echo Util::print_r($perm->getErrors());
}
} catch (Exception $e) {
echo 'Critical Error Code: ' . $e->getCode();
}
} else {
echo 'No Permissions';
}
}
My view links to the addPermission action by using a button:
echo CHtml::ajaxButton("Insert New Player", array('addPermission', 'server' => $server->serverID), array('success'=>'refresh'));
My function Util::checkServerPower(...) checks the current User of the Application. Consequence: Ajax requests in YII are handled by an Guest AuthWeb User, but i need to check whether the User is actually allowed to add permissions or not. I currently cannot think of a secured solution to protect malicious data send by other guests or not. Is it somehow possible to get the (server-side) userID of the Ajax-call?
Thanks anyway
sincerly
I would do it by using the built in access control and extending CWebUser.
It might seem lengthy but I think it's a clean solution. (We already have Yii::app()->user->isGuest and the like, so why not check all permissions here?)
1) Activate the access control filter.
(In one controller or in /components/controller.php for all your controllers at once)
public function filters()
{
return array( 'accessControl' ); // Tell Yii to use access rules for this controller
}
2) Add an access rule
In the concerned controller. (Sorry, I didn't bother with your index-action.)
public function accessRules()
{
return array(
[
'allow',
'actions'=>['AddPermission'],
'expression'=>'$user->has(Permission::MODIFY_SERVER)'
],
['deny'], // Deny everything else.
);
}
3) Extend CWebUser
// components/WebUser.php
class WebUser extends CWebUser {
public function has( $permission)
{
// Check database for permissions using Yii::app()->user->id
...
}
}
4) Configure your app to use your new WebUser instead of CWebUser
// config/main.php
'components'=>[
'user'=>[
'class' => 'WebUser',
],
],

Categories