mirror of
https://github.com/BookStackApp/BookStack.git
synced 2025-05-22 14:49:59 +08:00
Cleaned tests up, Started LDAP tests, Created LDAP wrapper
This commit is contained in:
@ -36,6 +36,14 @@ APP_URL=http://bookstack.dev
|
|||||||
# External services such as Gravatar
|
# External services such as Gravatar
|
||||||
DISABLE_EXTERNAL_SERVICES=false
|
DISABLE_EXTERNAL_SERVICES=false
|
||||||
|
|
||||||
|
# LDAP Settings
|
||||||
|
LDAP_SERVER=false
|
||||||
|
LDAP_BASE_DN=false
|
||||||
|
LDAP_DN=false
|
||||||
|
LDAP_PASS=false
|
||||||
|
LDAP_USER_FILTER=false
|
||||||
|
LDAP_VERSION=false
|
||||||
|
|
||||||
# Mail settings
|
# Mail settings
|
||||||
MAIL_DRIVER=smtp
|
MAIL_DRIVER=smtp
|
||||||
MAIL_HOST=localhost
|
MAIL_HOST=localhost
|
||||||
|
@ -118,17 +118,20 @@ class AuthController extends Controller
|
|||||||
*/
|
*/
|
||||||
protected function authenticated(Request $request, Authenticatable $user)
|
protected function authenticated(Request $request, Authenticatable $user)
|
||||||
{
|
{
|
||||||
if(!$user->exists && $user->email === null && !$request->has('email')) {
|
// Explicitly log them out for now if they do no exist.
|
||||||
|
if (!$user->exists) auth()->logout($user);
|
||||||
|
|
||||||
|
if (!$user->exists && $user->email === null && !$request->has('email')) {
|
||||||
$request->flash();
|
$request->flash();
|
||||||
session()->flash('request-email', true);
|
session()->flash('request-email', true);
|
||||||
return redirect('/login');
|
return redirect('/login');
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!$user->exists && $user->email === null && $request->has('email')) {
|
if (!$user->exists && $user->email === null && $request->has('email')) {
|
||||||
$user->email = $request->get('email');
|
$user->email = $request->get('email');
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!$user->exists) {
|
if (!$user->exists) {
|
||||||
$user->save();
|
$user->save();
|
||||||
$this->userRepo->attachDefaultRole($user);
|
$this->userRepo->attachDefaultRole($user);
|
||||||
auth()->login($user);
|
auth()->login($user);
|
||||||
|
@ -38,6 +38,7 @@ class Authenticate
|
|||||||
if(auth()->check() && auth()->user()->email_confirmed == false) {
|
if(auth()->check() && auth()->user()->email_confirmed == false) {
|
||||||
return redirect()->guest('/register/confirm/awaiting');
|
return redirect()->guest('/register/confirm/awaiting');
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->auth->guest() && !Setting::get('app-public')) {
|
if ($this->auth->guest() && !Setting::get('app-public')) {
|
||||||
if ($request->ajax()) {
|
if ($request->ajax()) {
|
||||||
return response('Unauthorized.', 401);
|
return response('Unauthorized.', 401);
|
||||||
|
@ -1,11 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
Route::get('/test', function() {
|
|
||||||
// TODO - remove this
|
|
||||||
$service = new \BookStack\Services\LdapService();
|
|
||||||
dd($service->getUserDetails('ksmith'));
|
|
||||||
});
|
|
||||||
|
|
||||||
// Authenticated routes...
|
// Authenticated routes...
|
||||||
Route::group(['middleware' => 'auth'], function () {
|
Route::group(['middleware' => 'auth'], function () {
|
||||||
|
|
||||||
|
@ -86,8 +86,10 @@ class LdapUserProvider implements UserProvider
|
|||||||
*/
|
*/
|
||||||
public function updateRememberToken(Authenticatable $user, $token)
|
public function updateRememberToken(Authenticatable $user, $token)
|
||||||
{
|
{
|
||||||
$user->setRememberToken($token);
|
if ($user->exists) {
|
||||||
$user->save();
|
$user->setRememberToken($token);
|
||||||
|
$user->save();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -113,6 +115,7 @@ class LdapUserProvider implements UserProvider
|
|||||||
$model->name = $userDetails['name'];
|
$model->name = $userDetails['name'];
|
||||||
$model->external_auth_id = $userDetails['uid'];
|
$model->external_auth_id = $userDetails['uid'];
|
||||||
$model->email = $userDetails['email'];
|
$model->email = $userDetails['email'];
|
||||||
|
$model->email_confirmed = true;
|
||||||
return $model;
|
return $model;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
86
app/Services/Ldap.php
Normal file
86
app/Services/Ldap.php
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
<?php namespace BookStack\Services;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class Ldap
|
||||||
|
* An object-orientated thin abstraction wrapper for common PHP LDAP functions.
|
||||||
|
* Allows the standard LDAP functions to be mocked for testing.
|
||||||
|
* @package BookStack\Services
|
||||||
|
*/
|
||||||
|
class Ldap
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Connect to a LDAP server.
|
||||||
|
* @param string $hostName
|
||||||
|
* @param int $port
|
||||||
|
* @return resource
|
||||||
|
*/
|
||||||
|
public function connect($hostName, $port)
|
||||||
|
{
|
||||||
|
return ldap_connect($hostName, $port);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the value of a LDAP option for the given connection.
|
||||||
|
* @param resource $ldapConnection
|
||||||
|
* @param int $option
|
||||||
|
* @param mixed $value
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function setOption($ldapConnection, $option, $value)
|
||||||
|
{
|
||||||
|
return ldap_set_option($ldapConnection, $option, $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search LDAP tree using the provided filter.
|
||||||
|
* @param resource $ldapConnection
|
||||||
|
* @param string $baseDn
|
||||||
|
* @param string $filter
|
||||||
|
* @param array|null $attributes
|
||||||
|
* @return resource
|
||||||
|
*/
|
||||||
|
public function search($ldapConnection, $baseDn, $filter, array $attributes = null)
|
||||||
|
{
|
||||||
|
return ldap_search($ldapConnection, $baseDn, $filter, $attributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get entries from an ldap search result.
|
||||||
|
* @param resource $ldapConnection
|
||||||
|
* @param resource $ldapSearchResult
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getEntries($ldapConnection, $ldapSearchResult)
|
||||||
|
{
|
||||||
|
return ldap_get_entries($ldapConnection, $ldapSearchResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search and get entries immediately.
|
||||||
|
* @param resource $ldapConnection
|
||||||
|
* @param string $baseDn
|
||||||
|
* @param string $filter
|
||||||
|
* @param array|null $attributes
|
||||||
|
* @return resource
|
||||||
|
*/
|
||||||
|
public function searchAndGetEntries($ldapConnection, $baseDn, $filter, array $attributes = null)
|
||||||
|
{
|
||||||
|
$search = $this->search($ldapConnection, $baseDn, $filter, $attributes);
|
||||||
|
return $this->getEntries($ldapConnection, $search);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bind to LDAP directory.
|
||||||
|
* @param resource $ldapConnection
|
||||||
|
* @param string $bindRdn
|
||||||
|
* @param string $bindPassword
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function bind($ldapConnection, $bindRdn = null, $bindPassword = null)
|
||||||
|
{
|
||||||
|
return ldap_bind($ldapConnection, $bindRdn, $bindPassword);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -4,10 +4,27 @@
|
|||||||
use BookStack\Exceptions\LdapException;
|
use BookStack\Exceptions\LdapException;
|
||||||
use Illuminate\Contracts\Auth\Authenticatable;
|
use Illuminate\Contracts\Auth\Authenticatable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class LdapService
|
||||||
|
* Handles any app-specific LDAP tasks.
|
||||||
|
* @package BookStack\Services
|
||||||
|
*/
|
||||||
class LdapService
|
class LdapService
|
||||||
{
|
{
|
||||||
|
|
||||||
|
protected $ldap;
|
||||||
protected $ldapConnection;
|
protected $ldapConnection;
|
||||||
|
protected $config;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* LdapService constructor.
|
||||||
|
* @param Ldap $ldap
|
||||||
|
*/
|
||||||
|
public function __construct(Ldap $ldap)
|
||||||
|
{
|
||||||
|
$this->ldap = $ldap;
|
||||||
|
$this->config = config('services.ldap');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the details of a user from LDAP using the given username.
|
* Get the details of a user from LDAP using the given username.
|
||||||
@ -21,17 +38,16 @@ class LdapService
|
|||||||
$ldapConnection = $this->getConnection();
|
$ldapConnection = $this->getConnection();
|
||||||
|
|
||||||
// Find user
|
// Find user
|
||||||
$userFilter = $this->buildFilter(config('services.ldap.user_filter'), ['user' => $userName]);
|
$userFilter = $this->buildFilter($this->config['user_filter'], ['user' => $userName]);
|
||||||
$baseDn = config('services.ldap.base_dn');
|
$baseDn = $this->config['base_dn'];
|
||||||
$ldapSearch = ldap_search($ldapConnection, $baseDn, $userFilter, ['cn', 'uid', 'dn', 'mail']);
|
$users = $this->ldap->searchAndGetEntries($ldapConnection, $baseDn, $userFilter, ['cn', 'uid', 'dn', 'mail']);
|
||||||
$users = ldap_get_entries($ldapConnection, $ldapSearch);
|
|
||||||
if ($users['count'] === 0) return null;
|
if ($users['count'] === 0) return null;
|
||||||
|
|
||||||
$user = $users[0];
|
$user = $users[0];
|
||||||
return [
|
return [
|
||||||
'uid' => $user['uid'][0],
|
'uid' => $user['uid'][0],
|
||||||
'name' => $user['cn'][0],
|
'name' => $user['cn'][0],
|
||||||
'dn' => $user['dn'],
|
'dn' => $user['dn'],
|
||||||
'email' => (isset($user['mail'])) ? $user['mail'][0] : null
|
'email' => (isset($user['mail'])) ? $user['mail'][0] : null
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@ -50,7 +66,12 @@ class LdapService
|
|||||||
if ($ldapUser['uid'] !== $user->external_auth_id) return false;
|
if ($ldapUser['uid'] !== $user->external_auth_id) return false;
|
||||||
|
|
||||||
$ldapConnection = $this->getConnection();
|
$ldapConnection = $this->getConnection();
|
||||||
$ldapBind = @ldap_bind($ldapConnection, $ldapUser['dn'], $password);
|
try {
|
||||||
|
$ldapBind = $this->ldap->bind($ldapConnection, $ldapUser['dn'], $password);
|
||||||
|
} catch (\ErrorException $e) {
|
||||||
|
$ldapBind = false;
|
||||||
|
}
|
||||||
|
|
||||||
return $ldapBind;
|
return $ldapBind;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,14 +83,14 @@ class LdapService
|
|||||||
*/
|
*/
|
||||||
protected function bindSystemUser($connection)
|
protected function bindSystemUser($connection)
|
||||||
{
|
{
|
||||||
$ldapDn = config('services.ldap.dn');
|
$ldapDn = $this->config['dn'];
|
||||||
$ldapPass = config('services.ldap.pass');
|
$ldapPass = $this->config['pass'];
|
||||||
|
|
||||||
$isAnonymous = ($ldapDn === false || $ldapPass === false);
|
$isAnonymous = ($ldapDn === false || $ldapPass === false);
|
||||||
if ($isAnonymous) {
|
if ($isAnonymous) {
|
||||||
$ldapBind = ldap_bind($connection);
|
$ldapBind = $this->ldap->bind($connection);
|
||||||
} else {
|
} else {
|
||||||
$ldapBind = ldap_bind($connection, $ldapDn, $ldapPass);
|
$ldapBind = $this->ldap->bind($connection, $ldapDn, $ldapPass);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$ldapBind) throw new LdapException('LDAP access failed using ' . $isAnonymous ? ' anonymous bind.' : ' given dn & pass details');
|
if (!$ldapBind) throw new LdapException('LDAP access failed using ' . $isAnonymous ? ' anonymous bind.' : ' given dn & pass details');
|
||||||
@ -86,20 +107,22 @@ class LdapService
|
|||||||
if ($this->ldapConnection !== null) return $this->ldapConnection;
|
if ($this->ldapConnection !== null) return $this->ldapConnection;
|
||||||
|
|
||||||
// Check LDAP extension in installed
|
// Check LDAP extension in installed
|
||||||
if (!function_exists('ldap_connect')) {
|
if (!function_exists('ldap_connect') && config('app.env') !== 'testing') {
|
||||||
throw new LdapException('LDAP PHP extension not installed');
|
throw new LdapException('LDAP PHP extension not installed');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get port from server string if specified.
|
// Get port from server string if specified.
|
||||||
$ldapServer = explode(':', config('services.ldap.server'));
|
$ldapServer = explode(':', $this->config['server']);
|
||||||
$ldapConnection = ldap_connect($ldapServer[0], count($ldapServer) > 1 ? $ldapServer[1] : 389);
|
$ldapConnection = $this->ldap->connect($ldapServer[0], count($ldapServer) > 1 ? $ldapServer[1] : 389);
|
||||||
|
|
||||||
if ($ldapConnection === false) {
|
if ($ldapConnection === false) {
|
||||||
throw new LdapException('Cannot connect to ldap server, Initial connection failed');
|
throw new LdapException('Cannot connect to ldap server, Initial connection failed');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set any required options
|
// Set any required options
|
||||||
ldap_set_option($ldapConnection, LDAP_OPT_PROTOCOL_VERSION, 3); // TODO - make configurable
|
if ($this->config['version']) {
|
||||||
|
$this->ldap->setOption($ldapConnection, LDAP_OPT_PROTOCOL_VERSION, $this->config['version']);
|
||||||
|
}
|
||||||
|
|
||||||
$this->ldapConnection = $ldapConnection;
|
$this->ldapConnection = $ldapConnection;
|
||||||
return $this->ldapConnection;
|
return $this->ldapConnection;
|
||||||
@ -107,7 +130,7 @@ class LdapService
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Build a filter string by injecting common variables.
|
* Build a filter string by injecting common variables.
|
||||||
* @param $filterString
|
* @param string $filterString
|
||||||
* @param array $attrs
|
* @param array $attrs
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
|
@ -54,7 +54,8 @@ return [
|
|||||||
'dn' => env('LDAP_DN', false),
|
'dn' => env('LDAP_DN', false),
|
||||||
'pass' => env('LDAP_PASS', false),
|
'pass' => env('LDAP_PASS', false),
|
||||||
'base_dn' => env('LDAP_BASE_DN', false),
|
'base_dn' => env('LDAP_BASE_DN', false),
|
||||||
'user_filter' => env('LDAP_USER_FILTER', '(&(uid=${user}))')
|
'user_filter' => env('LDAP_USER_FILTER', '(&(uid=${user}))'),
|
||||||
|
'version' => env('LDAP_VERSION', false)
|
||||||
]
|
]
|
||||||
|
|
||||||
];
|
];
|
||||||
|
@ -5,19 +5,19 @@
|
|||||||
|
|
||||||
<!-- Meta -->
|
<!-- Meta -->
|
||||||
<meta name="viewport" content="width=device-width">
|
<meta name="viewport" content="width=device-width">
|
||||||
|
<meta name="token" content="{{ csrf_token() }}">
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
|
|
||||||
<!-- Styles and Fonts -->
|
<!-- Styles and Fonts -->
|
||||||
<link rel="stylesheet" href="{{ versioned_asset('css/styles.css') }}">
|
<link rel="stylesheet" href="{{ versioned_asset('css/styles.css') }}">
|
||||||
<link rel="stylesheet" media="print" href="{{ versioned_asset('css/print-styles.css') }}">
|
<link rel="stylesheet" media="print" href="{{ versioned_asset('css/print-styles.css') }}">
|
||||||
<link href='//fonts.googleapis.com/css?family=Roboto:400,400italic,500,500italic,700,700italic,300italic,100,300' rel='stylesheet' type='text/css'>
|
|
||||||
<link rel="stylesheet" href="/libs/material-design-iconic-font/css/material-design-iconic-font.min.css">
|
<link rel="stylesheet" href="/libs/material-design-iconic-font/css/material-design-iconic-font.min.css">
|
||||||
|
|
||||||
<!-- Scripts -->
|
<!-- Scripts -->
|
||||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
|
<script src="/libs/jquery/jquery.min.js?version=2.1.4"></script>
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
<body class="@yield('body-class')" id="app">
|
<body class="@yield('body-class')" ng-app="bookStack">
|
||||||
|
|
||||||
@include('partials/notifications')
|
@include('partials/notifications')
|
||||||
|
|
||||||
@ -37,12 +37,15 @@
|
|||||||
@yield('header-buttons')
|
@yield('header-buttons')
|
||||||
</div>
|
</div>
|
||||||
@if(isset($signedIn) && $signedIn)
|
@if(isset($signedIn) && $signedIn)
|
||||||
<img class="avatar" src="{{$currentUser->getAvatar(30)}}" alt="{{ $currentUser->name }}">
|
<div class="dropdown-container" dropdown>
|
||||||
<div class="dropdown-container" data-dropdown>
|
<span class="user-name" dropdown-toggle>
|
||||||
<span class="user-name" data-dropdown-toggle>
|
<img class="avatar" src="{{$currentUser->getAvatar(30)}}" alt="{{ $currentUser->name }}">
|
||||||
{{ $currentUser->name }} <i class="zmdi zmdi-caret-down"></i>
|
<span class="name" ng-non-bindable>{{ $currentUser->name }}</span> <i class="zmdi zmdi-caret-down"></i>
|
||||||
</span>
|
</span>
|
||||||
<ul>
|
<ul>
|
||||||
|
<li>
|
||||||
|
<a href="/users/{{$currentUser->id}}" class="text-primary"><i class="zmdi zmdi-edit zmdi-hc-lg"></i>Edit Profile</a>
|
||||||
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="/logout" class="text-neg"><i class="zmdi zmdi-run zmdi-hc-lg"></i>Logout</a>
|
<a href="/logout" class="text-neg"><i class="zmdi zmdi-run zmdi-hc-lg"></i>Logout</a>
|
||||||
</li>
|
</li>
|
||||||
|
@ -7,7 +7,7 @@ use Illuminate\Foundation\Testing\DatabaseTransactions;
|
|||||||
class ActivityTrackingTest extends TestCase
|
class ActivityTrackingTest extends TestCase
|
||||||
{
|
{
|
||||||
|
|
||||||
public function testRecentlyViewedBooks()
|
public function test_recently_viewed_books()
|
||||||
{
|
{
|
||||||
$books = \BookStack\Book::all()->take(10);
|
$books = \BookStack\Book::all()->take(10);
|
||||||
|
|
||||||
@ -21,7 +21,7 @@ class ActivityTrackingTest extends TestCase
|
|||||||
->seeInElement('#recents', $books[1]->name);
|
->seeInElement('#recents', $books[1]->name);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testPopularBooks()
|
public function test_popular_books()
|
||||||
{
|
{
|
||||||
$books = \BookStack\Book::all()->take(10);
|
$books = \BookStack\Book::all()->take(10);
|
||||||
|
|
||||||
|
@ -5,23 +5,19 @@ use BookStack\EmailConfirmation;
|
|||||||
class AuthTest extends TestCase
|
class AuthTest extends TestCase
|
||||||
{
|
{
|
||||||
|
|
||||||
public function testAuthWorking()
|
public function test_auth_working()
|
||||||
{
|
{
|
||||||
$this->visit('/')
|
$this->visit('/')
|
||||||
->seePageIs('/login');
|
->seePageIs('/login');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testLogin()
|
public function test_login()
|
||||||
{
|
{
|
||||||
$this->visit('/')
|
|
||||||
->seePageIs('/login');
|
|
||||||
|
|
||||||
$this->login('admin@admin.com', 'password')
|
$this->login('admin@admin.com', 'password')
|
||||||
->seePageIs('/')
|
->seePageIs('/');
|
||||||
->see('BookStack');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testPublicViewing()
|
public function test_public_viewing()
|
||||||
{
|
{
|
||||||
$settings = app('BookStack\Services\SettingService');
|
$settings = app('BookStack\Services\SettingService');
|
||||||
$settings->put('app-public', 'true');
|
$settings->put('app-public', 'true');
|
||||||
@ -30,7 +26,7 @@ class AuthTest extends TestCase
|
|||||||
->see('Sign In');
|
->see('Sign In');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testRegistrationShowing()
|
public function test_registration_showing()
|
||||||
{
|
{
|
||||||
// Ensure registration form is showing
|
// Ensure registration form is showing
|
||||||
$this->setSettings(['registration-enabled' => 'true']);
|
$this->setSettings(['registration-enabled' => 'true']);
|
||||||
@ -40,7 +36,7 @@ class AuthTest extends TestCase
|
|||||||
->seePageIs('/register');
|
->seePageIs('/register');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testNormalRegistration()
|
public function test_normal_registration()
|
||||||
{
|
{
|
||||||
// Set settings and get user instance
|
// Set settings and get user instance
|
||||||
$this->setSettings(['registration-enabled' => 'true']);
|
$this->setSettings(['registration-enabled' => 'true']);
|
||||||
@ -58,7 +54,8 @@ class AuthTest extends TestCase
|
|||||||
->seeInDatabase('users', ['name' => $user->name, 'email' => $user->email]);
|
->seeInDatabase('users', ['name' => $user->name, 'email' => $user->email]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testConfirmedRegistration()
|
|
||||||
|
public function test_confirmed_registration()
|
||||||
{
|
{
|
||||||
// Set settings and get user instance
|
// Set settings and get user instance
|
||||||
$this->setSettings(['registration-enabled' => 'true', 'registration-confirmation' => 'true']);
|
$this->setSettings(['registration-enabled' => 'true', 'registration-confirmation' => 'true']);
|
||||||
@ -102,7 +99,32 @@ class AuthTest extends TestCase
|
|||||||
->seeInDatabase('users', ['name' => $user->name, 'email' => $user->email, 'email_confirmed' => true]);
|
->seeInDatabase('users', ['name' => $user->name, 'email' => $user->email, 'email_confirmed' => true]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testUserCreation()
|
public function test_restricted_registration()
|
||||||
|
{
|
||||||
|
$this->setSettings(['registration-enabled' => 'true', 'registration-confirmation' => 'true', 'registration-restrict' => 'example.com']);
|
||||||
|
$user = factory(\BookStack\User::class)->make();
|
||||||
|
// Go through registration process
|
||||||
|
$this->visit('/register')
|
||||||
|
->type($user->name, '#name')
|
||||||
|
->type($user->email, '#email')
|
||||||
|
->type($user->password, '#password')
|
||||||
|
->press('Create Account')
|
||||||
|
->seePageIs('/register')
|
||||||
|
->dontSeeInDatabase('users', ['email' => $user->email])
|
||||||
|
->see('That email domain does not have access to this application');
|
||||||
|
|
||||||
|
$user->email = 'barry@example.com';
|
||||||
|
|
||||||
|
$this->visit('/register')
|
||||||
|
->type($user->name, '#name')
|
||||||
|
->type($user->email, '#email')
|
||||||
|
->type($user->password, '#password')
|
||||||
|
->press('Create Account')
|
||||||
|
->seePageIs('/register/confirm')
|
||||||
|
->seeInDatabase('users', ['name' => $user->name, 'email' => $user->email, 'email_confirmed' => false]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_user_creation()
|
||||||
{
|
{
|
||||||
$user = factory(\BookStack\User::class)->make();
|
$user = factory(\BookStack\User::class)->make();
|
||||||
|
|
||||||
@ -120,7 +142,7 @@ class AuthTest extends TestCase
|
|||||||
->see($user->name);
|
->see($user->name);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testUserUpdating()
|
public function test_user_updating()
|
||||||
{
|
{
|
||||||
$user = \BookStack\User::all()->last();
|
$user = \BookStack\User::all()->last();
|
||||||
$password = $user->password;
|
$password = $user->password;
|
||||||
@ -136,7 +158,7 @@ class AuthTest extends TestCase
|
|||||||
->notSeeInDatabase('users', ['name' => $user->name]);
|
->notSeeInDatabase('users', ['name' => $user->name]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testUserPasswordUpdate()
|
public function test_user_password_update()
|
||||||
{
|
{
|
||||||
$user = \BookStack\User::all()->last();
|
$user = \BookStack\User::all()->last();
|
||||||
$userProfilePage = '/users/' . $user->id;
|
$userProfilePage = '/users/' . $user->id;
|
||||||
@ -156,7 +178,7 @@ class AuthTest extends TestCase
|
|||||||
$this->assertTrue(Hash::check('newpassword', $userPassword));
|
$this->assertTrue(Hash::check('newpassword', $userPassword));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testUserDeletion()
|
public function test_user_deletion()
|
||||||
{
|
{
|
||||||
$userDetails = factory(\BookStack\User::class)->make();
|
$userDetails = factory(\BookStack\User::class)->make();
|
||||||
$user = $this->getNewUser($userDetails->toArray());
|
$user = $this->getNewUser($userDetails->toArray());
|
||||||
@ -170,7 +192,7 @@ class AuthTest extends TestCase
|
|||||||
->notSeeInDatabase('users', ['name' => $user->name]);
|
->notSeeInDatabase('users', ['name' => $user->name]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testUserCannotBeDeletedIfLastAdmin()
|
public function test_user_cannot_be_deleted_if_last_admin()
|
||||||
{
|
{
|
||||||
$adminRole = \BookStack\Role::getRole('admin');
|
$adminRole = \BookStack\Role::getRole('admin');
|
||||||
// Ensure we currently only have 1 admin user
|
// Ensure we currently only have 1 admin user
|
||||||
@ -184,7 +206,7 @@ class AuthTest extends TestCase
|
|||||||
->see('You cannot delete the only admin');
|
->see('You cannot delete the only admin');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testLogout()
|
public function test_logout()
|
||||||
{
|
{
|
||||||
$this->asAdmin()
|
$this->asAdmin()
|
||||||
->visit('/')
|
->visit('/')
|
||||||
@ -200,7 +222,7 @@ class AuthTest extends TestCase
|
|||||||
* @param string $password
|
* @param string $password
|
||||||
* @return $this
|
* @return $this
|
||||||
*/
|
*/
|
||||||
private function login($email, $password)
|
protected function login($email, $password)
|
||||||
{
|
{
|
||||||
return $this->visit('/login')
|
return $this->visit('/login')
|
||||||
->type($email, '#email')
|
->type($email, '#email')
|
43
tests/Auth/LdapTest.php
Normal file
43
tests/Auth/LdapTest.php
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use BookStack\Services\LdapService;
|
||||||
|
use BookStack\User;
|
||||||
|
|
||||||
|
class LdapTest extends \TestCase
|
||||||
|
{
|
||||||
|
|
||||||
|
protected $mockLdap;
|
||||||
|
protected $mockUser;
|
||||||
|
protected $resourceId = 'resource-test';
|
||||||
|
|
||||||
|
public function setUp()
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
app('config')->set(['auth.method' => 'ldap', 'services.ldap.base_dn' => 'dc=ldap,dc=local', 'auth.providers.users.driver' => 'ldap']);
|
||||||
|
$this->mockLdap = Mockery::mock(BookStack\Services\Ldap::class);
|
||||||
|
$this->app['BookStack\Services\Ldap'] = $this->mockLdap;
|
||||||
|
$this->mockUser = factory(User::class)->make();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_ldap_login()
|
||||||
|
{
|
||||||
|
$this->mockLdap->shouldReceive('connect')->once()->andReturn($this->resourceId);
|
||||||
|
$this->mockLdap->shouldReceive('setOption')->once();
|
||||||
|
$this->mockLdap->shouldReceive('searchAndGetEntries')->twice()
|
||||||
|
->with($this->resourceId, config('services.ldap.base_dn'), Mockery::type('string'), Mockery::type('array'))
|
||||||
|
->andReturn(['count' => 1, 0 => [
|
||||||
|
'uid' => [$this->mockUser->name],
|
||||||
|
'cn' => [$this->mockUser->name],
|
||||||
|
'dn' => ['dc=test'.config('services.ldap.base_dn')]
|
||||||
|
]]);
|
||||||
|
$this->mockLdap->shouldReceive('bind')->times(1)->andReturn(true);
|
||||||
|
|
||||||
|
$this->visit('/login')
|
||||||
|
->see('Username')
|
||||||
|
->type($this->mockUser->name, '#username')
|
||||||
|
->type($this->mockUser->password, '#password')
|
||||||
|
->press('Sign In')
|
||||||
|
->seePageIs('/login')->see('Please enter an email to use for this account.');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -3,13 +3,13 @@
|
|||||||
class SocialAuthTest extends TestCase
|
class SocialAuthTest extends TestCase
|
||||||
{
|
{
|
||||||
|
|
||||||
public function testSocialRegistration()
|
public function test_social_registration()
|
||||||
{
|
{
|
||||||
// http://docs.mockery.io/en/latest/reference/startup_methods.html
|
// http://docs.mockery.io/en/latest/reference/startup_methods.html
|
||||||
$user = factory(\BookStack\User::class)->make();
|
$user = factory(\BookStack\User::class)->make();
|
||||||
|
|
||||||
$this->setSettings(['registration-enabled' => 'true']);
|
$this->setSettings(['registration-enabled' => 'true']);
|
||||||
$this->setEnvironment(['GOOGLE_APP_ID' => 'abc123', 'GOOGLE_APP_SECRET' => '123abc', 'APP_URL' => 'http://localhost']);
|
config(['GOOGLE_APP_ID' => 'abc123', 'GOOGLE_APP_SECRET' => '123abc', 'APP_URL' => 'http://localhost']);
|
||||||
|
|
||||||
$mockSocialite = Mockery::mock('Laravel\Socialite\Contracts\Factory');
|
$mockSocialite = Mockery::mock('Laravel\Socialite\Contracts\Factory');
|
||||||
$this->app['Laravel\Socialite\Contracts\Factory'] = $mockSocialite;
|
$this->app['Laravel\Socialite\Contracts\Factory'] = $mockSocialite;
|
||||||
@ -32,11 +32,4 @@ class SocialAuthTest extends TestCase
|
|||||||
$this->seeInDatabase('social_accounts', ['user_id' => $user->id]);
|
$this->seeInDatabase('social_accounts', ['user_id' => $user->id]);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function setEnvironment($array)
|
|
||||||
{
|
|
||||||
foreach ($array as $key => $value) {
|
|
||||||
putenv("$key=$value");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
@ -5,7 +5,7 @@ use Illuminate\Support\Facades\DB;
|
|||||||
class EntityTest extends TestCase
|
class EntityTest extends TestCase
|
||||||
{
|
{
|
||||||
|
|
||||||
public function testEntityCreation()
|
public function test_entity_creation()
|
||||||
{
|
{
|
||||||
|
|
||||||
// Test Creation
|
// Test Creation
|
||||||
@ -51,7 +51,7 @@ class EntityTest extends TestCase
|
|||||||
return \BookStack\Book::find($book->id);
|
return \BookStack\Book::find($book->id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testBookSortPageShows()
|
public function test_book_sort_page_shows()
|
||||||
{
|
{
|
||||||
$books = \BookStack\Book::all();
|
$books = \BookStack\Book::all();
|
||||||
$bookToSort = $books[0];
|
$bookToSort = $books[0];
|
||||||
@ -65,7 +65,7 @@ class EntityTest extends TestCase
|
|||||||
->see($books[1]->name);
|
->see($books[1]->name);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testBookSortItemReturnsBookContent()
|
public function test_book_sort_item_returns_book_content()
|
||||||
{
|
{
|
||||||
$books = \BookStack\Book::all();
|
$books = \BookStack\Book::all();
|
||||||
$bookToSort = $books[0];
|
$bookToSort = $books[0];
|
||||||
@ -155,7 +155,7 @@ class EntityTest extends TestCase
|
|||||||
return $book;
|
return $book;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testPageSearch()
|
public function test_page_search()
|
||||||
{
|
{
|
||||||
$book = \BookStack\Book::all()->first();
|
$book = \BookStack\Book::all()->first();
|
||||||
$page = $book->pages->first();
|
$page = $book->pages->first();
|
||||||
@ -170,7 +170,7 @@ class EntityTest extends TestCase
|
|||||||
->seePageIs($page->getUrl());
|
->seePageIs($page->getUrl());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testInvalidPageSearch()
|
public function test_invalid_page_search()
|
||||||
{
|
{
|
||||||
$this->asAdmin()
|
$this->asAdmin()
|
||||||
->visit('/')
|
->visit('/')
|
||||||
@ -180,7 +180,7 @@ class EntityTest extends TestCase
|
|||||||
->seeStatusCode(200);
|
->seeStatusCode(200);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testEmptySearchRedirectsBack()
|
public function test_empty_search_redirects_back()
|
||||||
{
|
{
|
||||||
$this->asAdmin()
|
$this->asAdmin()
|
||||||
->visit('/')
|
->visit('/')
|
||||||
@ -188,7 +188,7 @@ class EntityTest extends TestCase
|
|||||||
->seePageIs('/');
|
->seePageIs('/');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testBookSearch()
|
public function test_book_search()
|
||||||
{
|
{
|
||||||
$book = \BookStack\Book::all()->first();
|
$book = \BookStack\Book::all()->first();
|
||||||
$page = $book->pages->last();
|
$page = $book->pages->last();
|
||||||
@ -202,7 +202,7 @@ class EntityTest extends TestCase
|
|||||||
->see($chapter->name);
|
->see($chapter->name);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testEmptyBookSearchRedirectsBack()
|
public function test_empty_book_search_redirects_back()
|
||||||
{
|
{
|
||||||
$book = \BookStack\Book::all()->first();
|
$book = \BookStack\Book::all()->first();
|
||||||
$this->asAdmin()
|
$this->asAdmin()
|
||||||
@ -212,7 +212,7 @@ class EntityTest extends TestCase
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public function testEntitiesViewableAfterCreatorDeletion()
|
public function test_entities_viewable_after_creator_deletion()
|
||||||
{
|
{
|
||||||
// Create required assets and revisions
|
// Create required assets and revisions
|
||||||
$creator = $this->getNewUser();
|
$creator = $this->getNewUser();
|
||||||
@ -225,7 +225,7 @@ class EntityTest extends TestCase
|
|||||||
$this->checkEntitiesViewable($entities);
|
$this->checkEntitiesViewable($entities);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testEntitiesViewableAfterUpdaterDeletion()
|
public function test_entities_viewable_after_updater_deletion()
|
||||||
{
|
{
|
||||||
// Create required assets and revisions
|
// Create required assets and revisions
|
||||||
$creator = $this->getNewUser();
|
$creator = $this->getNewUser();
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
class PublicViewTest extends TestCase
|
class PublicViewTest extends TestCase
|
||||||
{
|
{
|
||||||
|
|
||||||
public function testBooksViewable()
|
public function test_books_viewable()
|
||||||
{
|
{
|
||||||
$this->setSettings(['app-public' => 'true']);
|
$this->setSettings(['app-public' => 'true']);
|
||||||
$books = \BookStack\Book::orderBy('name', 'asc')->take(10)->get();
|
$books = \BookStack\Book::orderBy('name', 'asc')->take(10)->get();
|
||||||
@ -13,14 +13,14 @@ class PublicViewTest extends TestCase
|
|||||||
$this->visit('/books')
|
$this->visit('/books')
|
||||||
->seeStatusCode(200)
|
->seeStatusCode(200)
|
||||||
->see($books[0]->name)
|
->see($books[0]->name)
|
||||||
// Check indavidual book page is showing and it's child contents are visible.
|
// Check individual book page is showing and it's child contents are visible.
|
||||||
->click($bookToVisit->name)
|
->click($bookToVisit->name)
|
||||||
->seePageIs($bookToVisit->getUrl())
|
->seePageIs($bookToVisit->getUrl())
|
||||||
->see($bookToVisit->name)
|
->see($bookToVisit->name)
|
||||||
->see($bookToVisit->chapters()->first()->name);
|
->see($bookToVisit->chapters()->first()->name);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testChaptersViewable()
|
public function test_chapters_viewable()
|
||||||
{
|
{
|
||||||
$this->setSettings(['app-public' => 'true']);
|
$this->setSettings(['app-public' => 'true']);
|
||||||
$chapterToVisit = \BookStack\Chapter::first();
|
$chapterToVisit = \BookStack\Chapter::first();
|
||||||
@ -30,7 +30,7 @@ class PublicViewTest extends TestCase
|
|||||||
$this->visit($chapterToVisit->getUrl())
|
$this->visit($chapterToVisit->getUrl())
|
||||||
->seeStatusCode(200)
|
->seeStatusCode(200)
|
||||||
->see($chapterToVisit->name)
|
->see($chapterToVisit->name)
|
||||||
// Check indavidual chapter page is showing and it's child contents are visible.
|
// Check individual chapter page is showing and it's child contents are visible.
|
||||||
->see($pageToVisit->name)
|
->see($pageToVisit->name)
|
||||||
->click($pageToVisit->name)
|
->click($pageToVisit->name)
|
||||||
->see($chapterToVisit->book->name)
|
->see($chapterToVisit->book->name)
|
||||||
|
Reference in New Issue
Block a user