Commit 4d1112b7 authored by stanislav.goldmann's avatar stanislav.goldmann

Initial Commit

parents
.editorconfig
.idea/
\ No newline at end of file
# 1. Create an AuthController
Create your Authentication Controller manually or use
`php artisan make:controller AuthController`
```php
<?php
namespace App\Http\Controllers;
use App\Http\Requests\LoginRequest;
use Illuminate\Contracts\Auth\Guard;
use Krenor\LdapAuth\Objects\LdapUser;
class AuthController extends Controller
{
/**
* the model instance
*
* @var User
*/
protected $user;
/**
* The Guard implementation.
*
* @var Authenticator
*/
protected $auth;
/**
* Create a new authentication controller instance.
*
* @param Authenticator|Guard $auth
* @param LdapUser $user
*/
public function __construct(Guard $auth, LdapUser $user)
{
$this->auth = $auth;
$this->user = $user;
}
/**
* Show the application login form.
*
* @return Response
*/
public function getLogin()
{
return view('login');
}
/**
* Handle a login request to the application.
*
* @param LoginRequest $request
*
* @return Response
*/
public function postLogin(LoginRequest $request)
{
if ( $this->auth->attempt( $request->only('username', 'password') ) ) {
// Redirect to indented page or fall back to index page
return redirect()->intended('/');
}
return redirect()->back()->withErrors(
'Username and/or Password are not matching!'
);
}
/**
* Log the user out of the application.
*
* @return Response
*/
public function getLogout()
{
$this->auth->logout();
return redirect('/');
}
}
```
You can create your own request rules as mine basically
use `required` on both username and password.
# 2. Configure the routes
```php
Route::get('/', function () {
return view('home');
});
Route::get('/login', 'AuthController@getLogin');
Route::post('/login', 'AuthController@postLogin');
Route::get('/logout', 'AuthController@getLogout');
// Register your Routes to be accessed only with authentication
Route::group(['middleware' => 'auth'], function () {
Route::get('mysite', function () {
return view('foo');
});
});
```
You're set to go! Now create some Views and try it out. :)
# (3. Tweak Laravels Authentication)
As I don't like the default path for a login to be `/auth/login` feel free to change
this file at line 41 : `ROOT/app/Http/Middleware/Authenticate.php`
`return redirect()->guest('/login');`
\ No newline at end of file
# The MIT License (MIT)
Copyright (c) 2015 Stanislav Goldmann <stanislav.goldmann@gmail.com>
> Permission is hereby granted, free of charge, to any person obtaining a copy
> of this software and associated documentation files (the "Software"), to deal
> in the Software without restriction, including without limitation the rights
> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> copies of the Software, and to permit persons to whom the Software is
> furnished to do so, subject to the following conditions:
>
> The above copyright notice and this permission notice shall be included in
> all copies or substantial portions of the Software.
>
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
> THE SOFTWARE.
\ No newline at end of file
[![Latest Stable Version](https://img.shields.io/packagist/v/krenor/ldap-auth.svg?style=flat-square)](https://packagist.org/packages/krenor/ldap-auth)
[![License](https://img.shields.io/packagist/l/krenor/ldap-auth.svg?style=flat-square)](https://packagist.org/packages/krenor/ldap-auth)
# ldap-auth
Very basic **READ ONLY** LDAP authentication driver for [Laravel 5.1.11+](http://laravel.com/)
## Installation
### Step 1: Install Through Composer
Add to your root composer.json and install with `composer install` or `composer update`
{
require: {
"krenor/ldap-auth": "~1.0"
}
}
or use `composer require krenor/ldap-auth` in your console.
### Step 2: Add the Service Provider
Modify your `config/app.php` file and add the service provider to the providers array.
'Krenor\LdapAuth\LdapAuthServiceProvider::class,'
## Configuration
### Step 1: Tweak the basic authentication
Update your `config/auth.php` to use **ldap** as authentication and the **LdapUser** Class.
'driver' => 'ldap',
'model' => Krenor\LdapAuth\Objects\LdapUser::class,
### Step 2: Create an LDAP config
Add a **ldap.php** to your config directory.
It should look like this.
```php
<?php
return [
'suffix' => '@example.local',
'domain_controller' => 'dns.example.local',
'base_dn' => 'OU=People,DC=example,DC=local',
'ssl' => false, // if using TLS this MUST be false
'tls' => false, // // if using SSL this MUST be false
'admin_user' => 'admin', // Prevent anonymous bindings
'admin_pass' => 'admin' // Prevent anonymous bindings
];
```
You may use an array of Domain Controllers instead of a single one.
```php
'domain_controller' => ['dns1.example.local', 'dns2.example.local']
```
## Usage
### Authentication
Look up here for an [Example](https://github.com/krenor/ldap-auth/blob/master/EXAMPLE.md) or
Look up here for all [Guard methods](https://github.com/neoascetic/laravel-framework/blob/master/src/Illuminate/Auth/Guard.php) using `$this->auth`.
## Contributing
### Pull Requests
- **[PSR-2 Coding Standard](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md)**
- **Add tests** - Your patch won't be accepted if it doesn't have tests.
- **Document any changes** - Make sure the `README.md` and any other relevant documentation are kept up-to-date.
- **Create feature branches** - Use `git checkout -b my-new-feature`
- **One pull request per feature** - If you want to do more than one thing, send multiple pull requests.
- **Send coherent history** - Make sure each individual commit in your pull request is meaningful. If you had to make multiple intermediate commits while developing, please [squash them](http://www.git-scm.com/book/en/v2/Git-Tools-Rewriting-History#Changing-Multiple-Commit-Messages) before submitting.
## Licence
ldap-auth is distributed under the terms of the [MIT license](https://github.com/krenor/ldap-auth/blob/master/LICENSE.md)
\ No newline at end of file
{
"name": "krenor/ldap-auth",
"description": "Basic readonly authentication via LDAP for Laravel 5.1",
"keywords": [
"laravel 5.1.11", "laravel", "laravel auth", "laravel authentication",
"ldap", "ldap authentication", "active directory",
"krenor"
],
"license": "MIT",
"authors": [
{
"name": "Stanislav Goldmann",
"email": "stanislav.goldmann@gmail.com",
"role": "Developer",
"homepage": "https://github.com/krenor"
}
],
"require": {
"laravel/framework": "~5.1.11",
"php": ">=5.5.9"
},
"require-dev": {
"phpunit/phpunit" : "4.*"
},
"autoload": {
"psr-4": {
"Krenor\\LdapAuth\\": "src/"
}
}
}
<?php
namespace Krenor\LdapAuth\Connections;
use ErrorException;
use Krenor\LdapAuth\Contracts\ConnectionInterface;
use Krenor\LdapAuth\Exceptions\ConnectionException;
class LdapConnection implements ConnectionInterface
{
/**
* Indicates whether or not to use SSL
*
* @var bool
*/
protected $ssl = false;
/**
* Indicates whether or not to use TLS
* If it's used ensure that ssl is set to false and vice-versa
*
* @var bool
*/
protected $tls = false;
/**
* The current LDAP Connection
*
* @var resource
*/
protected $connection;
/**
* Indicates whether or not the current connection is bound
* @var bool
*/
protected $bound = false;
/**
* Constructor
*
* @param array $config
*/
public function __construct(array $config)
{
if($config['tls']) $this->tls = true;
if($config['ssl']) $this->ssl = true;
}
/**
* Initialises a Connection via hostname
*
* @param string $hostname
*
* @return resource
*/
public function connect($hostname)
{
$protocol = $this->ssl ? $this::PROTOCOL_SSL : $this::PROTOCOL;
$port = $this->ssl ? $this::PORT_SSL : $this::PORT;
return $this->connection = ldap_connect($protocol . $hostname, $port);
}
/**
* Binds LDAP connection to the server
*
* @param $username
* @param $password
*
* @return bool
*
* @throws ConnectionException
*/
public function bind($username, $password)
{
// Tries to run the LDAP Connection as TLS
if($this->tls){
if(!ldap_start_tls($this->connection)){
throw new ConnectionException('Unable to Connect to LDAP using TLS.');
}
}
try{
$this->bound = ldap_bind($this->connection, $username, $password);
}
catch(ErrorException $e){
$this->bound = false;
}
return $this->bound;
}
/**
* @param $option
* @param $value
* @return bool
*/
public function option($option, $value)
{
return ldap_set_option($this->connection, $option, $value);
}
/**
* @return string
*/
public function error()
{
return ldap_error($this->connection);
}
/**
* @param string $dn
* @param string $filter
* @param array $fields
* @return resource
*/
public function search($dn, $filter, array $fields)
{
return ldap_search($this->connection, $dn, $filter, $fields);
}
/**
* @param $result
* @return array
*/
public function entry($result)
{
return ldap_get_entries($this->connection, $result);
}
/**
* @return bool
*/
public function bound()
{
return $this->bound;
}
/**
* @return bool
*/
public function ssl()
{
return $this->ssl;
}
/**
* @return bool
*/
public function tls()
{
return $this->tls;
}
/**
* @return resource
*/
public function connection()
{
return $this->connection;
}
}
\ No newline at end of file
<?php
namespace Krenor\LdapAuth\Contracts;
interface ConnectionInterface
{
/**
* The SSL LDAP protocol string.
*
* @var string
*/
const PROTOCOL_SSL = 'ldaps://';
/**
* The non-SSL LDAP protocol string.
*
* @var string
*/
const PROTOCOL = 'ldap://';
/**
* The LDAP SSL Port number.
*
* @var string
*/
const PORT_SSL = '636';
/**
* The non SSL LDAP port number.
*
* @var string
*/
const PORT = '389';
/**
* LDAP Protocol Version
*
* @var integer
*/
const VERSION = 3;
/**
* Whether to automatically follow referrals returned by the LDAP server
*
* @var boolean
*/
const REFERRALS = false;
/**
* Connects the specified hostname to the LDAP server
*
* @param string $hostname
*
* @return resource
*/
public function connect($hostname);
/**
* Binds the LDAP connection to the server with login credentials
*
* @param $username
* @param $password
*
* @return bool
*/
public function bind($username, $password);
/**
* Sets an option key value pair for the current connection
*
* @param $option
* @param $value
*
* @return bool
*/
public function option($option, $value);
/**
* Searches in LDAP with the scope of LDAP_SCOPE_SUBTREE
*
* @param string $dn
* @param string $filter
* @param array $fields
*
* @return array
*/
public function search($dn, $filter, array $fields);
/**
* Check if connection is bound
*
* @return bool
*/
public function bound();
/**
* Check if connection is using tls
*
* @return bool
*/
public function tls();
/**
* Check if connection is using ssl
*
* @return bool
*/
public function ssl();
/**
* Retrieve last error occurrence
*
* @return string
*/
public function error();
/**
* Retrieve current LDAP connection
*
* @return resource
*/
public function connection();
/**
* Retrieve LDAP Entry
*
* @param $resultset
*
* @return array
*/
public function entry($resultset);
}
\ No newline at end of file
<?php
namespace Krenor\LdapAuth\Contracts;
interface UserInterface
{
/**
* Build an LdapUser object from the LDAP entry
*
* @param array $entry
* @return void
*/
public function build(array $entry);
/**
* Check if the LdapUser is a member of requested group
*
* @param string $group
* @return bool
*/
public function isMemberOf($group);
}
\ No newline at end of file
<?php
namespace Krenor\LdapAuth\Exceptions;
use Exception;
class ConnectionException extends Exception {}
\ No newline at end of file
<?php
namespace Krenor\LdapAuth\Exceptions;
use Exception;
class MissingConfigurationException extends Exception
{
public function __construct()
{
parent::__construct("Please ensure that a ldap.php file is present in the config/ root directory.");
}
}
\ No newline at end of file
<?php
namespace Krenor\LdapAuth;
use Krenor\LdapAuth\Connections\LdapConnection;
use Krenor\LdapAuth\Contracts\ConnectionInterface;
use Krenor\LdapAuth\Exceptions\MissingConfigurationException;
class Ldap {
/**
* The account suffix for the domain domain
*
* @var string
*/
protected $suffix;
/**
* The base distinguished name for the domain
*
* @var string
*/
protected $base_dn;
/**
* Array of domain controller(s) to balance LDAP queries
*
* @var array
*/
protected $domain_controller = [];
/**
* If no anonymous login is allowed
*
* @var string
*/
private $admin_user;
/**
* If no anonymous login is allowed
*
* @var string
*/
private $admin_pass;
/**
* Current LDAP Connection
*
* @var LdapConnection
*/
protected $ldap;
/**
* Default fields to fetch a search or read by
*
* @var array
*/
protected $fields = ['samaccountname', 'displayname', 'memberof'];
/**
* Default filter to execute a search query on
*
* @var string
*/
private $search_filter = "sAMAccountName";
/**
* Tries to connect and bind to the LDAP
*
* @param array $options
*
* @throws MissingConfigurationException
*/
public function __construct($options)
{
$config = $this->bindConfig($options);
// Build Common Name from Config file and append to base DN
$this->admin_user = 'CN=' . $this->admin_user . ',' . $this->base_dn;
$this->ldap = new LdapConnection($config);
$this->connect($this->ldap);
}
/**
* Initializes the connecting parameters.
* The actual connect happens with $this->ldap->bind()
*
* @param ConnectionInterface $connection
*
* @throws Exceptions\ConnectionException
*/
protected function connect(ConnectionInterface $connection)
{
$dc = is_array($this->domain_controller) ? array_rand($this->domain_controller) : $this->domain_controller;
$this->ldap->connect($dc);
$this->ldap->option(LDAP_OPT_PROTOCOL_VERSION, $connection::PROTOCOL);
$this->ldap->option(LDAP_OPT_REFERRALS, $connection::REFERRALS);
$this->ldap->bind($this->admin_user, $this->admin_pass);
}
/**
* Execute a search query in the entire LDAP tree
*
* @param string $filter msdn.microsoft.com/En-US/library/aa746475.aspx
* @param array $fields specific attributes to be returned. Defaults are set
* as $fields in this class. DN is always returned, no matter what.
*
* @return array $entry|null
*/
public function find($filter, array $fields = [])
{
$results = $this->ldap->search(
$this->base_dn,
$this->search_filter . '=' . $filter,
($fields ? $fields : $this->fields)
);
if(count($results) > 0){
$entry = $this->ldap->entry($results);
// Returning a single LDAP entry
if(isset($entry[0]) && !empty($entry[0])) {
return $entry[0];
}
}
return null;
}
/**
* Rebinds with a given DN and Password
*
* @param string $username
* @param string $password
*
* @return bool
*
* @throws Exceptions\ConnectionException
*/
public function auth($username, $password)
{
return $this->ldap->bind($username, $password);
}
/**
* Bind configuration file to class properties
* as long as these already exist
*
* @param array $config Complete config
*
* @return array $config Striped config
*/
private function bindConfig(array $config)
{
foreach($config as $key => $value){
if(property_exists($this, $key) ){
$this->{$key} = $value;
// Remove config key
unset($config[$key]);
}
}
// Every non-property key is left over and returned
return $config;
}
}
\ No newline at end of file
<?php
namespace Krenor\LdapAuth;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\ServiceProvider;
use Krenor\LdapAuth\Exceptions\MissingConfigurationException;
class LdapAuthServiceProvider extends ServiceProvider
{
/**
* Indicates if loading of the provider is deferred.
*
* @var bool
*/
protected $defer = false;
/**
* Perform post-registration booting of services.
*
* @return void
*/
public function boot()
{
// Register 'ldap' as authentication method
Auth::extend('ldap', function($app){
// Create new LDAP connection based on configuration files
$ldap = new Ldap( $this->getLdapConfig() );
return new LdapAuthUserProvider(
$ldap, $app['config']['auth']['model']
);
});
}
/**
* Register any package services.
*
* @return void
*/
public function register()
{
//
}
/**
* Get the services provided by the provider.
*
* @return array
*/
public function provides()
{
return ['auth'];
}
/**
* @return array
*
* @throws MissingConfigurationException
*/
private function getLdapConfig()
{
if( is_array($this->app['config']['ldap']) ){
return $this->app['config']['ldap'];
}
throw new MissingConfigurationException();
}
}
\ No newline at end of file
<?php
namespace Krenor\LdapAuth;
use Illuminate\Contracts\Auth\UserProvider;
use Illuminate\Contracts\Auth\Authenticatable;
class LdapAuthUserProvider implements UserProvider
{
/**
* LDAP Object
*
* @var object
*/
protected $ldap;
/**
* Auth User Class
*
* @var string
*/
protected $model;
/**
* @param Ldap $ldap
* @param string $model
*/
public function __construct(Ldap $ldap, $model)
{
$this->ldap = $ldap;
$this->model = $model;
}
/**
* Retrieve a user by their unique identifier.
*
* @param mixed $identifier
* @return \Illuminate\Contracts\Auth\Authenticatable|null
*/
public function retrieveById($identifier)
{
return $this->retrieveByCredentials(
['username' => $identifier]
);
}
/**
* Retrieve a user by their unique identifier and "remember me" token.
*
* @param mixed $identifier
* @param string $token
* @return \Illuminate\Contracts\Auth\Authenticatable|null
*/
public function retrieveByToken($identifier, $token)
{
// this shouldn't be needed as user / password is in ldap
}
/**
* Update the "remember me" token for the given user in storage.
*
* @param \Illuminate\Contracts\Auth\Authenticatable $user
* @param string $token
* @return void
*/
public function updateRememberToken(Authenticatable $user, $token)
{
// this shouldn't be needed as user / password is in ldap
}
/**
* Retrieve a user by the given credentials.
*
* @param array $credentials
* @return \Krenor\LdapAuth\Objects\LdapUser|null
*/
public function retrieveByCredentials(array $credentials)
{
$username = $credentials['username'];
$result = $this->ldap->find($username);
if( !is_null($result) ){
$user = new $this->model;
$user->build( $result );
return $user;
}
return null;
}
/**
* Validate a user against the given credentials.
*
* @param \Illuminate\Contracts\Auth\Authenticatable $user
* @param array $credentials
* @return bool
*/
public function validateCredentials(Authenticatable $user, array $credentials)
{
return $this->ldap->auth(
$user->dn,
$credentials['password']
);
}
}
\ No newline at end of file
<?php
namespace Krenor\LdapAuth\Objects;
use Illuminate\Contracts\Auth\Authenticatable as UserContract;
use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract;
use Krenor\LdapAuth\Contracts\UserInterface as LdapUserContract;
use Illuminate\Foundation\Auth\Access\Authorizable;
class LdapUser implements UserContract, AuthorizableContract, LdapUserContract
{
use Authorizable;
/**
* Most of the ldap user's attributes.
*
* @var array
*/
protected $attributes;
/**
* Build an LdapUser object from the LDAP entry
*
* @param array $entry
* @return void
*/
public function build(array $entry)
{
$this->buildAttributesFromLdap( $entry );
}
/**
* Get the unique identifier for the user.
*
* @return mixed
*/
public function getAuthIdentifier()
{
return $this->attributes['samaccountname'];
}
/**
* Get the password for the user.
*
* @return string
*/
public function getAuthPassword()
{
// this shouldn't be needed as you cannot directly access the password
}
/**
* Get the token value for the "remember me" session.
*
* @return string
*/
public function getRememberToken()
{
// this shouldn't be needed as user / password is in ldap
}
/**
* Set the token value for the "remember me" session.
*
* @param string $value
* @return void
*/
public function setRememberToken($value)
{
// this shouldn't be needed as user / password is in ldap
}
/**
* Get the column name for the "remember me" token.
*
* @return string
*/
public function getRememberTokenName()
{
// this shouldn't be needed as user / password is in ldap
}
/**
* Dynamically access the user's attributes.
*
* @param string $key
* @return mixed
*/
public function __get($key)
{
return $this->attributes[$key];
}
/**
* Dynamically set an attribute on the user.
*
* @param string $key
* @param mixed $value
* @return void
*/
public function __set($key, $value)
{
$this->attributes[$key] = $value;
}
/**
* Dynamically check if a value is set on the user.
*
* @param string $key
* @return bool
*/
public function __isset($key)
{
return isset( $this->attributes[$key] );
}
/**
* Setting of the LdapUser attributes
*
* @param array $entry
*/
private function buildAttributesFromLdap($entry)
{
$this->attributes['display_name'] = $entry['displayname'][0];
$this->attributes['samaccountname'] = $entry['samaccountname'][0];
$this->attributes['dn'] = $entry['dn'];
$this->attributes['member_of'] = $entry['memberof'];
// Just for readability, unsetting count as we only fetch one user
unset( $this->attributes['member_of']['count'] );
}
/**
* Check if the LdapUser is a member of requested group
*
* @param string $group
* @return bool
*/
public function isMemberOf($group)
{
foreach($this->attributes['member_of'] as $groups) {
if( preg_match('/^CN=' . $group . '/', $groups) ) {
return true;
}
}
return false;
}
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment