Showing
15 changed files
with
259 additions
and
79 deletions
| ... | @@ -36,6 +36,14 @@ APP_URL=http://bookstack.dev | ... | @@ -36,6 +36,14 @@ APP_URL=http://bookstack.dev |
| 36 | # External services such as Gravatar | 36 | # External services such as Gravatar |
| 37 | DISABLE_EXTERNAL_SERVICES=false | 37 | DISABLE_EXTERNAL_SERVICES=false |
| 38 | 38 | ||
| 39 | +# LDAP Settings | ||
| 40 | +LDAP_SERVER=false | ||
| 41 | +LDAP_BASE_DN=false | ||
| 42 | +LDAP_DN=false | ||
| 43 | +LDAP_PASS=false | ||
| 44 | +LDAP_USER_FILTER=false | ||
| 45 | +LDAP_VERSION=false | ||
| 46 | + | ||
| 39 | # Mail settings | 47 | # Mail settings |
| 40 | MAIL_DRIVER=smtp | 48 | MAIL_DRIVER=smtp |
| 41 | MAIL_HOST=localhost | 49 | MAIL_HOST=localhost | ... | ... |
| ... | @@ -118,17 +118,20 @@ class AuthController extends Controller | ... | @@ -118,17 +118,20 @@ class AuthController extends Controller |
| 118 | */ | 118 | */ |
| 119 | protected function authenticated(Request $request, Authenticatable $user) | 119 | protected function authenticated(Request $request, Authenticatable $user) |
| 120 | { | 120 | { |
| 121 | - if(!$user->exists && $user->email === null && !$request->has('email')) { | 121 | + // Explicitly log them out for now if they do no exist. |
| 122 | + if (!$user->exists) auth()->logout($user); | ||
| 123 | + | ||
| 124 | + if (!$user->exists && $user->email === null && !$request->has('email')) { | ||
| 122 | $request->flash(); | 125 | $request->flash(); |
| 123 | session()->flash('request-email', true); | 126 | session()->flash('request-email', true); |
| 124 | return redirect('/login'); | 127 | return redirect('/login'); |
| 125 | } | 128 | } |
| 126 | 129 | ||
| 127 | - if(!$user->exists && $user->email === null && $request->has('email')) { | 130 | + if (!$user->exists && $user->email === null && $request->has('email')) { |
| 128 | $user->email = $request->get('email'); | 131 | $user->email = $request->get('email'); |
| 129 | } | 132 | } |
| 130 | 133 | ||
| 131 | - if(!$user->exists) { | 134 | + if (!$user->exists) { |
| 132 | $user->save(); | 135 | $user->save(); |
| 133 | $this->userRepo->attachDefaultRole($user); | 136 | $this->userRepo->attachDefaultRole($user); |
| 134 | auth()->login($user); | 137 | auth()->login($user); | ... | ... |
| ... | @@ -38,6 +38,7 @@ class Authenticate | ... | @@ -38,6 +38,7 @@ class Authenticate |
| 38 | if(auth()->check() && auth()->user()->email_confirmed == false) { | 38 | if(auth()->check() && auth()->user()->email_confirmed == false) { |
| 39 | return redirect()->guest('/register/confirm/awaiting'); | 39 | return redirect()->guest('/register/confirm/awaiting'); |
| 40 | } | 40 | } |
| 41 | + | ||
| 41 | if ($this->auth->guest() && !Setting::get('app-public')) { | 42 | if ($this->auth->guest() && !Setting::get('app-public')) { |
| 42 | if ($request->ajax()) { | 43 | if ($request->ajax()) { |
| 43 | return response('Unauthorized.', 401); | 44 | return response('Unauthorized.', 401); | ... | ... |
| 1 | <?php | 1 | <?php |
| 2 | 2 | ||
| 3 | -Route::get('/test', function() { | ||
| 4 | - // TODO - remove this | ||
| 5 | - $service = new \BookStack\Services\LdapService(); | ||
| 6 | - dd($service->getUserDetails('ksmith')); | ||
| 7 | -}); | ||
| 8 | - | ||
| 9 | // Authenticated routes... | 3 | // Authenticated routes... |
| 10 | Route::group(['middleware' => 'auth'], function () { | 4 | Route::group(['middleware' => 'auth'], function () { |
| 11 | 5 | ... | ... |
| ... | @@ -86,8 +86,10 @@ class LdapUserProvider implements UserProvider | ... | @@ -86,8 +86,10 @@ class LdapUserProvider implements UserProvider |
| 86 | */ | 86 | */ |
| 87 | public function updateRememberToken(Authenticatable $user, $token) | 87 | public function updateRememberToken(Authenticatable $user, $token) |
| 88 | { | 88 | { |
| 89 | - $user->setRememberToken($token); | 89 | + if ($user->exists) { |
| 90 | - $user->save(); | 90 | + $user->setRememberToken($token); |
| 91 | + $user->save(); | ||
| 92 | + } | ||
| 91 | } | 93 | } |
| 92 | 94 | ||
| 93 | /** | 95 | /** |
| ... | @@ -113,6 +115,7 @@ class LdapUserProvider implements UserProvider | ... | @@ -113,6 +115,7 @@ class LdapUserProvider implements UserProvider |
| 113 | $model->name = $userDetails['name']; | 115 | $model->name = $userDetails['name']; |
| 114 | $model->external_auth_id = $userDetails['uid']; | 116 | $model->external_auth_id = $userDetails['uid']; |
| 115 | $model->email = $userDetails['email']; | 117 | $model->email = $userDetails['email']; |
| 118 | + $model->email_confirmed = true; | ||
| 116 | return $model; | 119 | return $model; |
| 117 | } | 120 | } |
| 118 | 121 | ... | ... |
app/Services/Ldap.php
0 → 100644
| 1 | +<?php namespace BookStack\Services; | ||
| 2 | + | ||
| 3 | + | ||
| 4 | +/** | ||
| 5 | + * Class Ldap | ||
| 6 | + * An object-orientated thin abstraction wrapper for common PHP LDAP functions. | ||
| 7 | + * Allows the standard LDAP functions to be mocked for testing. | ||
| 8 | + * @package BookStack\Services | ||
| 9 | + */ | ||
| 10 | +class Ldap | ||
| 11 | +{ | ||
| 12 | + | ||
| 13 | + /** | ||
| 14 | + * Connect to a LDAP server. | ||
| 15 | + * @param string $hostName | ||
| 16 | + * @param int $port | ||
| 17 | + * @return resource | ||
| 18 | + */ | ||
| 19 | + public function connect($hostName, $port) | ||
| 20 | + { | ||
| 21 | + return ldap_connect($hostName, $port); | ||
| 22 | + } | ||
| 23 | + | ||
| 24 | + /** | ||
| 25 | + * Set the value of a LDAP option for the given connection. | ||
| 26 | + * @param resource $ldapConnection | ||
| 27 | + * @param int $option | ||
| 28 | + * @param mixed $value | ||
| 29 | + * @return bool | ||
| 30 | + */ | ||
| 31 | + public function setOption($ldapConnection, $option, $value) | ||
| 32 | + { | ||
| 33 | + return ldap_set_option($ldapConnection, $option, $value); | ||
| 34 | + } | ||
| 35 | + | ||
| 36 | + /** | ||
| 37 | + * Search LDAP tree using the provided filter. | ||
| 38 | + * @param resource $ldapConnection | ||
| 39 | + * @param string $baseDn | ||
| 40 | + * @param string $filter | ||
| 41 | + * @param array|null $attributes | ||
| 42 | + * @return resource | ||
| 43 | + */ | ||
| 44 | + public function search($ldapConnection, $baseDn, $filter, array $attributes = null) | ||
| 45 | + { | ||
| 46 | + return ldap_search($ldapConnection, $baseDn, $filter, $attributes); | ||
| 47 | + } | ||
| 48 | + | ||
| 49 | + /** | ||
| 50 | + * Get entries from an ldap search result. | ||
| 51 | + * @param resource $ldapConnection | ||
| 52 | + * @param resource $ldapSearchResult | ||
| 53 | + * @return array | ||
| 54 | + */ | ||
| 55 | + public function getEntries($ldapConnection, $ldapSearchResult) | ||
| 56 | + { | ||
| 57 | + return ldap_get_entries($ldapConnection, $ldapSearchResult); | ||
| 58 | + } | ||
| 59 | + | ||
| 60 | + /** | ||
| 61 | + * Search and get entries immediately. | ||
| 62 | + * @param resource $ldapConnection | ||
| 63 | + * @param string $baseDn | ||
| 64 | + * @param string $filter | ||
| 65 | + * @param array|null $attributes | ||
| 66 | + * @return resource | ||
| 67 | + */ | ||
| 68 | + public function searchAndGetEntries($ldapConnection, $baseDn, $filter, array $attributes = null) | ||
| 69 | + { | ||
| 70 | + $search = $this->search($ldapConnection, $baseDn, $filter, $attributes); | ||
| 71 | + return $this->getEntries($ldapConnection, $search); | ||
| 72 | + } | ||
| 73 | + | ||
| 74 | + /** | ||
| 75 | + * Bind to LDAP directory. | ||
| 76 | + * @param resource $ldapConnection | ||
| 77 | + * @param string $bindRdn | ||
| 78 | + * @param string $bindPassword | ||
| 79 | + * @return bool | ||
| 80 | + */ | ||
| 81 | + public function bind($ldapConnection, $bindRdn = null, $bindPassword = null) | ||
| 82 | + { | ||
| 83 | + return ldap_bind($ldapConnection, $bindRdn, $bindPassword); | ||
| 84 | + } | ||
| 85 | + | ||
| 86 | +} | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
| ... | @@ -4,10 +4,27 @@ | ... | @@ -4,10 +4,27 @@ |
| 4 | use BookStack\Exceptions\LdapException; | 4 | use BookStack\Exceptions\LdapException; |
| 5 | use Illuminate\Contracts\Auth\Authenticatable; | 5 | use Illuminate\Contracts\Auth\Authenticatable; |
| 6 | 6 | ||
| 7 | +/** | ||
| 8 | + * Class LdapService | ||
| 9 | + * Handles any app-specific LDAP tasks. | ||
| 10 | + * @package BookStack\Services | ||
| 11 | + */ | ||
| 7 | class LdapService | 12 | class LdapService |
| 8 | { | 13 | { |
| 9 | 14 | ||
| 15 | + protected $ldap; | ||
| 10 | protected $ldapConnection; | 16 | protected $ldapConnection; |
| 17 | + protected $config; | ||
| 18 | + | ||
| 19 | + /** | ||
| 20 | + * LdapService constructor. | ||
| 21 | + * @param Ldap $ldap | ||
| 22 | + */ | ||
| 23 | + public function __construct(Ldap $ldap) | ||
| 24 | + { | ||
| 25 | + $this->ldap = $ldap; | ||
| 26 | + $this->config = config('services.ldap'); | ||
| 27 | + } | ||
| 11 | 28 | ||
| 12 | /** | 29 | /** |
| 13 | * Get the details of a user from LDAP using the given username. | 30 | * Get the details of a user from LDAP using the given username. |
| ... | @@ -21,17 +38,16 @@ class LdapService | ... | @@ -21,17 +38,16 @@ class LdapService |
| 21 | $ldapConnection = $this->getConnection(); | 38 | $ldapConnection = $this->getConnection(); |
| 22 | 39 | ||
| 23 | // Find user | 40 | // Find user |
| 24 | - $userFilter = $this->buildFilter(config('services.ldap.user_filter'), ['user' => $userName]); | 41 | + $userFilter = $this->buildFilter($this->config['user_filter'], ['user' => $userName]); |
| 25 | - $baseDn = config('services.ldap.base_dn'); | 42 | + $baseDn = $this->config['base_dn']; |
| 26 | - $ldapSearch = ldap_search($ldapConnection, $baseDn, $userFilter, ['cn', 'uid', 'dn', 'mail']); | 43 | + $users = $this->ldap->searchAndGetEntries($ldapConnection, $baseDn, $userFilter, ['cn', 'uid', 'dn', 'mail']); |
| 27 | - $users = ldap_get_entries($ldapConnection, $ldapSearch); | ||
| 28 | if ($users['count'] === 0) return null; | 44 | if ($users['count'] === 0) return null; |
| 29 | 45 | ||
| 30 | $user = $users[0]; | 46 | $user = $users[0]; |
| 31 | return [ | 47 | return [ |
| 32 | - 'uid' => $user['uid'][0], | 48 | + 'uid' => $user['uid'][0], |
| 33 | - 'name' => $user['cn'][0], | 49 | + 'name' => $user['cn'][0], |
| 34 | - 'dn' => $user['dn'], | 50 | + 'dn' => $user['dn'], |
| 35 | 'email' => (isset($user['mail'])) ? $user['mail'][0] : null | 51 | 'email' => (isset($user['mail'])) ? $user['mail'][0] : null |
| 36 | ]; | 52 | ]; |
| 37 | } | 53 | } |
| ... | @@ -50,7 +66,12 @@ class LdapService | ... | @@ -50,7 +66,12 @@ class LdapService |
| 50 | if ($ldapUser['uid'] !== $user->external_auth_id) return false; | 66 | if ($ldapUser['uid'] !== $user->external_auth_id) return false; |
| 51 | 67 | ||
| 52 | $ldapConnection = $this->getConnection(); | 68 | $ldapConnection = $this->getConnection(); |
| 53 | - $ldapBind = @ldap_bind($ldapConnection, $ldapUser['dn'], $password); | 69 | + try { |
| 70 | + $ldapBind = $this->ldap->bind($ldapConnection, $ldapUser['dn'], $password); | ||
| 71 | + } catch (\ErrorException $e) { | ||
| 72 | + $ldapBind = false; | ||
| 73 | + } | ||
| 74 | + | ||
| 54 | return $ldapBind; | 75 | return $ldapBind; |
| 55 | } | 76 | } |
| 56 | 77 | ||
| ... | @@ -62,14 +83,14 @@ class LdapService | ... | @@ -62,14 +83,14 @@ class LdapService |
| 62 | */ | 83 | */ |
| 63 | protected function bindSystemUser($connection) | 84 | protected function bindSystemUser($connection) |
| 64 | { | 85 | { |
| 65 | - $ldapDn = config('services.ldap.dn'); | 86 | + $ldapDn = $this->config['dn']; |
| 66 | - $ldapPass = config('services.ldap.pass'); | 87 | + $ldapPass = $this->config['pass']; |
| 67 | 88 | ||
| 68 | $isAnonymous = ($ldapDn === false || $ldapPass === false); | 89 | $isAnonymous = ($ldapDn === false || $ldapPass === false); |
| 69 | if ($isAnonymous) { | 90 | if ($isAnonymous) { |
| 70 | - $ldapBind = ldap_bind($connection); | 91 | + $ldapBind = $this->ldap->bind($connection); |
| 71 | } else { | 92 | } else { |
| 72 | - $ldapBind = ldap_bind($connection, $ldapDn, $ldapPass); | 93 | + $ldapBind = $this->ldap->bind($connection, $ldapDn, $ldapPass); |
| 73 | } | 94 | } |
| 74 | 95 | ||
| 75 | if (!$ldapBind) throw new LdapException('LDAP access failed using ' . $isAnonymous ? ' anonymous bind.' : ' given dn & pass details'); | 96 | if (!$ldapBind) throw new LdapException('LDAP access failed using ' . $isAnonymous ? ' anonymous bind.' : ' given dn & pass details'); |
| ... | @@ -86,20 +107,22 @@ class LdapService | ... | @@ -86,20 +107,22 @@ class LdapService |
| 86 | if ($this->ldapConnection !== null) return $this->ldapConnection; | 107 | if ($this->ldapConnection !== null) return $this->ldapConnection; |
| 87 | 108 | ||
| 88 | // Check LDAP extension in installed | 109 | // Check LDAP extension in installed |
| 89 | - if (!function_exists('ldap_connect')) { | 110 | + if (!function_exists('ldap_connect') && config('app.env') !== 'testing') { |
| 90 | throw new LdapException('LDAP PHP extension not installed'); | 111 | throw new LdapException('LDAP PHP extension not installed'); |
| 91 | } | 112 | } |
| 92 | 113 | ||
| 93 | // Get port from server string if specified. | 114 | // Get port from server string if specified. |
| 94 | - $ldapServer = explode(':', config('services.ldap.server')); | 115 | + $ldapServer = explode(':', $this->config['server']); |
| 95 | - $ldapConnection = ldap_connect($ldapServer[0], count($ldapServer) > 1 ? $ldapServer[1] : 389); | 116 | + $ldapConnection = $this->ldap->connect($ldapServer[0], count($ldapServer) > 1 ? $ldapServer[1] : 389); |
| 96 | 117 | ||
| 97 | if ($ldapConnection === false) { | 118 | if ($ldapConnection === false) { |
| 98 | throw new LdapException('Cannot connect to ldap server, Initial connection failed'); | 119 | throw new LdapException('Cannot connect to ldap server, Initial connection failed'); |
| 99 | } | 120 | } |
| 100 | 121 | ||
| 101 | // Set any required options | 122 | // Set any required options |
| 102 | - ldap_set_option($ldapConnection, LDAP_OPT_PROTOCOL_VERSION, 3); // TODO - make configurable | 123 | + if ($this->config['version']) { |
| 124 | + $this->ldap->setOption($ldapConnection, LDAP_OPT_PROTOCOL_VERSION, $this->config['version']); | ||
| 125 | + } | ||
| 103 | 126 | ||
| 104 | $this->ldapConnection = $ldapConnection; | 127 | $this->ldapConnection = $ldapConnection; |
| 105 | return $this->ldapConnection; | 128 | return $this->ldapConnection; |
| ... | @@ -107,7 +130,7 @@ class LdapService | ... | @@ -107,7 +130,7 @@ class LdapService |
| 107 | 130 | ||
| 108 | /** | 131 | /** |
| 109 | * Build a filter string by injecting common variables. | 132 | * Build a filter string by injecting common variables. |
| 110 | - * @param $filterString | 133 | + * @param string $filterString |
| 111 | * @param array $attrs | 134 | * @param array $attrs |
| 112 | * @return string | 135 | * @return string |
| 113 | */ | 136 | */ | ... | ... |
| ... | @@ -54,7 +54,8 @@ return [ | ... | @@ -54,7 +54,8 @@ return [ |
| 54 | 'dn' => env('LDAP_DN', false), | 54 | 'dn' => env('LDAP_DN', false), |
| 55 | 'pass' => env('LDAP_PASS', false), | 55 | 'pass' => env('LDAP_PASS', false), |
| 56 | 'base_dn' => env('LDAP_BASE_DN', false), | 56 | 'base_dn' => env('LDAP_BASE_DN', false), |
| 57 | - 'user_filter' => env('LDAP_USER_FILTER', '(&(uid=${user}))') | 57 | + 'user_filter' => env('LDAP_USER_FILTER', '(&(uid=${user}))'), |
| 58 | + 'version' => env('LDAP_VERSION', false) | ||
| 58 | ] | 59 | ] |
| 59 | 60 | ||
| 60 | ]; | 61 | ]; | ... | ... |
| ... | @@ -5,19 +5,19 @@ | ... | @@ -5,19 +5,19 @@ |
| 5 | 5 | ||
| 6 | <!-- Meta --> | 6 | <!-- Meta --> |
| 7 | <meta name="viewport" content="width=device-width"> | 7 | <meta name="viewport" content="width=device-width"> |
| 8 | + <meta name="token" content="{{ csrf_token() }}"> | ||
| 8 | <meta charset="utf-8"> | 9 | <meta charset="utf-8"> |
| 9 | 10 | ||
| 10 | <!-- Styles and Fonts --> | 11 | <!-- Styles and Fonts --> |
| 11 | <link rel="stylesheet" href="{{ versioned_asset('css/styles.css') }}"> | 12 | <link rel="stylesheet" href="{{ versioned_asset('css/styles.css') }}"> |
| 12 | <link rel="stylesheet" media="print" href="{{ versioned_asset('css/print-styles.css') }}"> | 13 | <link rel="stylesheet" media="print" href="{{ versioned_asset('css/print-styles.css') }}"> |
| 13 | - <link href='//fonts.googleapis.com/css?family=Roboto:400,400italic,500,500italic,700,700italic,300italic,100,300' rel='stylesheet' type='text/css'> | ||
| 14 | <link rel="stylesheet" href="/libs/material-design-iconic-font/css/material-design-iconic-font.min.css"> | 14 | <link rel="stylesheet" href="/libs/material-design-iconic-font/css/material-design-iconic-font.min.css"> |
| 15 | 15 | ||
| 16 | <!-- Scripts --> | 16 | <!-- Scripts --> |
| 17 | - <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script> | 17 | + <script src="/libs/jquery/jquery.min.js?version=2.1.4"></script> |
| 18 | 18 | ||
| 19 | </head> | 19 | </head> |
| 20 | -<body class="@yield('body-class')" id="app"> | 20 | +<body class="@yield('body-class')" ng-app="bookStack"> |
| 21 | 21 | ||
| 22 | @include('partials/notifications') | 22 | @include('partials/notifications') |
| 23 | 23 | ||
| ... | @@ -37,13 +37,16 @@ | ... | @@ -37,13 +37,16 @@ |
| 37 | @yield('header-buttons') | 37 | @yield('header-buttons') |
| 38 | </div> | 38 | </div> |
| 39 | @if(isset($signedIn) && $signedIn) | 39 | @if(isset($signedIn) && $signedIn) |
| 40 | - <img class="avatar" src="{{$currentUser->getAvatar(30)}}" alt="{{ $currentUser->name }}"> | 40 | + <div class="dropdown-container" dropdown> |
| 41 | - <div class="dropdown-container" data-dropdown> | 41 | + <span class="user-name" dropdown-toggle> |
| 42 | - <span class="user-name" data-dropdown-toggle> | 42 | + <img class="avatar" src="{{$currentUser->getAvatar(30)}}" alt="{{ $currentUser->name }}"> |
| 43 | - {{ $currentUser->name }} <i class="zmdi zmdi-caret-down"></i> | 43 | + <span class="name" ng-non-bindable>{{ $currentUser->name }}</span> <i class="zmdi zmdi-caret-down"></i> |
| 44 | </span> | 44 | </span> |
| 45 | <ul> | 45 | <ul> |
| 46 | <li> | 46 | <li> |
| 47 | + <a href="/users/{{$currentUser->id}}" class="text-primary"><i class="zmdi zmdi-edit zmdi-hc-lg"></i>Edit Profile</a> | ||
| 48 | + </li> | ||
| 49 | + <li> | ||
| 47 | <a href="/logout" class="text-neg"><i class="zmdi zmdi-run zmdi-hc-lg"></i>Logout</a> | 50 | <a href="/logout" class="text-neg"><i class="zmdi zmdi-run zmdi-hc-lg"></i>Logout</a> |
| 48 | </li> | 51 | </li> |
| 49 | </ul> | 52 | </ul> | ... | ... |
| ... | @@ -7,7 +7,7 @@ use Illuminate\Foundation\Testing\DatabaseTransactions; | ... | @@ -7,7 +7,7 @@ use Illuminate\Foundation\Testing\DatabaseTransactions; |
| 7 | class ActivityTrackingTest extends TestCase | 7 | class ActivityTrackingTest extends TestCase |
| 8 | { | 8 | { |
| 9 | 9 | ||
| 10 | - public function testRecentlyViewedBooks() | 10 | + public function test_recently_viewed_books() |
| 11 | { | 11 | { |
| 12 | $books = \BookStack\Book::all()->take(10); | 12 | $books = \BookStack\Book::all()->take(10); |
| 13 | 13 | ||
| ... | @@ -21,7 +21,7 @@ class ActivityTrackingTest extends TestCase | ... | @@ -21,7 +21,7 @@ class ActivityTrackingTest extends TestCase |
| 21 | ->seeInElement('#recents', $books[1]->name); | 21 | ->seeInElement('#recents', $books[1]->name); |
| 22 | } | 22 | } |
| 23 | 23 | ||
| 24 | - public function testPopularBooks() | 24 | + public function test_popular_books() |
| 25 | { | 25 | { |
| 26 | $books = \BookStack\Book::all()->take(10); | 26 | $books = \BookStack\Book::all()->take(10); |
| 27 | 27 | ... | ... |
| ... | @@ -5,23 +5,19 @@ use BookStack\EmailConfirmation; | ... | @@ -5,23 +5,19 @@ use BookStack\EmailConfirmation; |
| 5 | class AuthTest extends TestCase | 5 | class AuthTest extends TestCase |
| 6 | { | 6 | { |
| 7 | 7 | ||
| 8 | - public function testAuthWorking() | 8 | + public function test_auth_working() |
| 9 | { | 9 | { |
| 10 | $this->visit('/') | 10 | $this->visit('/') |
| 11 | ->seePageIs('/login'); | 11 | ->seePageIs('/login'); |
| 12 | } | 12 | } |
| 13 | 13 | ||
| 14 | - public function testLogin() | 14 | + public function test_login() |
| 15 | { | 15 | { |
| 16 | - $this->visit('/') | ||
| 17 | - ->seePageIs('/login'); | ||
| 18 | - | ||
| 19 | $this->login('admin@admin.com', 'password') | 16 | $this->login('admin@admin.com', 'password') |
| 20 | - ->seePageIs('/') | 17 | + ->seePageIs('/'); |
| 21 | - ->see('BookStack'); | ||
| 22 | } | 18 | } |
| 23 | 19 | ||
| 24 | - public function testPublicViewing() | 20 | + public function test_public_viewing() |
| 25 | { | 21 | { |
| 26 | $settings = app('BookStack\Services\SettingService'); | 22 | $settings = app('BookStack\Services\SettingService'); |
| 27 | $settings->put('app-public', 'true'); | 23 | $settings->put('app-public', 'true'); |
| ... | @@ -30,7 +26,7 @@ class AuthTest extends TestCase | ... | @@ -30,7 +26,7 @@ class AuthTest extends TestCase |
| 30 | ->see('Sign In'); | 26 | ->see('Sign In'); |
| 31 | } | 27 | } |
| 32 | 28 | ||
| 33 | - public function testRegistrationShowing() | 29 | + public function test_registration_showing() |
| 34 | { | 30 | { |
| 35 | // Ensure registration form is showing | 31 | // Ensure registration form is showing |
| 36 | $this->setSettings(['registration-enabled' => 'true']); | 32 | $this->setSettings(['registration-enabled' => 'true']); |
| ... | @@ -40,7 +36,7 @@ class AuthTest extends TestCase | ... | @@ -40,7 +36,7 @@ class AuthTest extends TestCase |
| 40 | ->seePageIs('/register'); | 36 | ->seePageIs('/register'); |
| 41 | } | 37 | } |
| 42 | 38 | ||
| 43 | - public function testNormalRegistration() | 39 | + public function test_normal_registration() |
| 44 | { | 40 | { |
| 45 | // Set settings and get user instance | 41 | // Set settings and get user instance |
| 46 | $this->setSettings(['registration-enabled' => 'true']); | 42 | $this->setSettings(['registration-enabled' => 'true']); |
| ... | @@ -58,7 +54,8 @@ class AuthTest extends TestCase | ... | @@ -58,7 +54,8 @@ class AuthTest extends TestCase |
| 58 | ->seeInDatabase('users', ['name' => $user->name, 'email' => $user->email]); | 54 | ->seeInDatabase('users', ['name' => $user->name, 'email' => $user->email]); |
| 59 | } | 55 | } |
| 60 | 56 | ||
| 61 | - public function testConfirmedRegistration() | 57 | + |
| 58 | + public function test_confirmed_registration() | ||
| 62 | { | 59 | { |
| 63 | // Set settings and get user instance | 60 | // Set settings and get user instance |
| 64 | $this->setSettings(['registration-enabled' => 'true', 'registration-confirmation' => 'true']); | 61 | $this->setSettings(['registration-enabled' => 'true', 'registration-confirmation' => 'true']); |
| ... | @@ -102,7 +99,32 @@ class AuthTest extends TestCase | ... | @@ -102,7 +99,32 @@ class AuthTest extends TestCase |
| 102 | ->seeInDatabase('users', ['name' => $user->name, 'email' => $user->email, 'email_confirmed' => true]); | 99 | ->seeInDatabase('users', ['name' => $user->name, 'email' => $user->email, 'email_confirmed' => true]); |
| 103 | } | 100 | } |
| 104 | 101 | ||
| 105 | - public function testUserCreation() | 102 | + public function test_restricted_registration() |
| 103 | + { | ||
| 104 | + $this->setSettings(['registration-enabled' => 'true', 'registration-confirmation' => 'true', 'registration-restrict' => 'example.com']); | ||
| 105 | + $user = factory(\BookStack\User::class)->make(); | ||
| 106 | + // Go through registration process | ||
| 107 | + $this->visit('/register') | ||
| 108 | + ->type($user->name, '#name') | ||
| 109 | + ->type($user->email, '#email') | ||
| 110 | + ->type($user->password, '#password') | ||
| 111 | + ->press('Create Account') | ||
| 112 | + ->seePageIs('/register') | ||
| 113 | + ->dontSeeInDatabase('users', ['email' => $user->email]) | ||
| 114 | + ->see('That email domain does not have access to this application'); | ||
| 115 | + | ||
| 116 | + $user->email = 'barry@example.com'; | ||
| 117 | + | ||
| 118 | + $this->visit('/register') | ||
| 119 | + ->type($user->name, '#name') | ||
| 120 | + ->type($user->email, '#email') | ||
| 121 | + ->type($user->password, '#password') | ||
| 122 | + ->press('Create Account') | ||
| 123 | + ->seePageIs('/register/confirm') | ||
| 124 | + ->seeInDatabase('users', ['name' => $user->name, 'email' => $user->email, 'email_confirmed' => false]); | ||
| 125 | + } | ||
| 126 | + | ||
| 127 | + public function test_user_creation() | ||
| 106 | { | 128 | { |
| 107 | $user = factory(\BookStack\User::class)->make(); | 129 | $user = factory(\BookStack\User::class)->make(); |
| 108 | 130 | ||
| ... | @@ -120,7 +142,7 @@ class AuthTest extends TestCase | ... | @@ -120,7 +142,7 @@ class AuthTest extends TestCase |
| 120 | ->see($user->name); | 142 | ->see($user->name); |
| 121 | } | 143 | } |
| 122 | 144 | ||
| 123 | - public function testUserUpdating() | 145 | + public function test_user_updating() |
| 124 | { | 146 | { |
| 125 | $user = \BookStack\User::all()->last(); | 147 | $user = \BookStack\User::all()->last(); |
| 126 | $password = $user->password; | 148 | $password = $user->password; |
| ... | @@ -136,7 +158,7 @@ class AuthTest extends TestCase | ... | @@ -136,7 +158,7 @@ class AuthTest extends TestCase |
| 136 | ->notSeeInDatabase('users', ['name' => $user->name]); | 158 | ->notSeeInDatabase('users', ['name' => $user->name]); |
| 137 | } | 159 | } |
| 138 | 160 | ||
| 139 | - public function testUserPasswordUpdate() | 161 | + public function test_user_password_update() |
| 140 | { | 162 | { |
| 141 | $user = \BookStack\User::all()->last(); | 163 | $user = \BookStack\User::all()->last(); |
| 142 | $userProfilePage = '/users/' . $user->id; | 164 | $userProfilePage = '/users/' . $user->id; |
| ... | @@ -156,7 +178,7 @@ class AuthTest extends TestCase | ... | @@ -156,7 +178,7 @@ class AuthTest extends TestCase |
| 156 | $this->assertTrue(Hash::check('newpassword', $userPassword)); | 178 | $this->assertTrue(Hash::check('newpassword', $userPassword)); |
| 157 | } | 179 | } |
| 158 | 180 | ||
| 159 | - public function testUserDeletion() | 181 | + public function test_user_deletion() |
| 160 | { | 182 | { |
| 161 | $userDetails = factory(\BookStack\User::class)->make(); | 183 | $userDetails = factory(\BookStack\User::class)->make(); |
| 162 | $user = $this->getNewUser($userDetails->toArray()); | 184 | $user = $this->getNewUser($userDetails->toArray()); |
| ... | @@ -170,7 +192,7 @@ class AuthTest extends TestCase | ... | @@ -170,7 +192,7 @@ class AuthTest extends TestCase |
| 170 | ->notSeeInDatabase('users', ['name' => $user->name]); | 192 | ->notSeeInDatabase('users', ['name' => $user->name]); |
| 171 | } | 193 | } |
| 172 | 194 | ||
| 173 | - public function testUserCannotBeDeletedIfLastAdmin() | 195 | + public function test_user_cannot_be_deleted_if_last_admin() |
| 174 | { | 196 | { |
| 175 | $adminRole = \BookStack\Role::getRole('admin'); | 197 | $adminRole = \BookStack\Role::getRole('admin'); |
| 176 | // Ensure we currently only have 1 admin user | 198 | // Ensure we currently only have 1 admin user |
| ... | @@ -184,7 +206,7 @@ class AuthTest extends TestCase | ... | @@ -184,7 +206,7 @@ class AuthTest extends TestCase |
| 184 | ->see('You cannot delete the only admin'); | 206 | ->see('You cannot delete the only admin'); |
| 185 | } | 207 | } |
| 186 | 208 | ||
| 187 | - public function testLogout() | 209 | + public function test_logout() |
| 188 | { | 210 | { |
| 189 | $this->asAdmin() | 211 | $this->asAdmin() |
| 190 | ->visit('/') | 212 | ->visit('/') |
| ... | @@ -200,7 +222,7 @@ class AuthTest extends TestCase | ... | @@ -200,7 +222,7 @@ class AuthTest extends TestCase |
| 200 | * @param string $password | 222 | * @param string $password |
| 201 | * @return $this | 223 | * @return $this |
| 202 | */ | 224 | */ |
| 203 | - private function login($email, $password) | 225 | + protected function login($email, $password) |
| 204 | { | 226 | { |
| 205 | return $this->visit('/login') | 227 | return $this->visit('/login') |
| 206 | ->type($email, '#email') | 228 | ->type($email, '#email') | ... | ... |
tests/Auth/LdapTest.php
0 → 100644
| 1 | +<?php | ||
| 2 | + | ||
| 3 | +use BookStack\Services\LdapService; | ||
| 4 | +use BookStack\User; | ||
| 5 | + | ||
| 6 | +class LdapTest extends \TestCase | ||
| 7 | +{ | ||
| 8 | + | ||
| 9 | + protected $mockLdap; | ||
| 10 | + protected $mockUser; | ||
| 11 | + protected $resourceId = 'resource-test'; | ||
| 12 | + | ||
| 13 | + public function setUp() | ||
| 14 | + { | ||
| 15 | + parent::setUp(); | ||
| 16 | + app('config')->set(['auth.method' => 'ldap', 'services.ldap.base_dn' => 'dc=ldap,dc=local', 'auth.providers.users.driver' => 'ldap']); | ||
| 17 | + $this->mockLdap = Mockery::mock(BookStack\Services\Ldap::class); | ||
| 18 | + $this->app['BookStack\Services\Ldap'] = $this->mockLdap; | ||
| 19 | + $this->mockUser = factory(User::class)->make(); | ||
| 20 | + } | ||
| 21 | + | ||
| 22 | + public function test_ldap_login() | ||
| 23 | + { | ||
| 24 | + $this->mockLdap->shouldReceive('connect')->once()->andReturn($this->resourceId); | ||
| 25 | + $this->mockLdap->shouldReceive('setOption')->once(); | ||
| 26 | + $this->mockLdap->shouldReceive('searchAndGetEntries')->twice() | ||
| 27 | + ->with($this->resourceId, config('services.ldap.base_dn'), Mockery::type('string'), Mockery::type('array')) | ||
| 28 | + ->andReturn(['count' => 1, 0 => [ | ||
| 29 | + 'uid' => [$this->mockUser->name], | ||
| 30 | + 'cn' => [$this->mockUser->name], | ||
| 31 | + 'dn' => ['dc=test'.config('services.ldap.base_dn')] | ||
| 32 | + ]]); | ||
| 33 | + $this->mockLdap->shouldReceive('bind')->times(1)->andReturn(true); | ||
| 34 | + | ||
| 35 | + $this->visit('/login') | ||
| 36 | + ->see('Username') | ||
| 37 | + ->type($this->mockUser->name, '#username') | ||
| 38 | + ->type($this->mockUser->password, '#password') | ||
| 39 | + ->press('Sign In') | ||
| 40 | + ->seePageIs('/login')->see('Please enter an email to use for this account.'); | ||
| 41 | + } | ||
| 42 | + | ||
| 43 | +} | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
| ... | @@ -3,13 +3,13 @@ | ... | @@ -3,13 +3,13 @@ |
| 3 | class SocialAuthTest extends TestCase | 3 | class SocialAuthTest extends TestCase |
| 4 | { | 4 | { |
| 5 | 5 | ||
| 6 | - public function testSocialRegistration() | 6 | + public function test_social_registration() |
| 7 | { | 7 | { |
| 8 | // http://docs.mockery.io/en/latest/reference/startup_methods.html | 8 | // http://docs.mockery.io/en/latest/reference/startup_methods.html |
| 9 | $user = factory(\BookStack\User::class)->make(); | 9 | $user = factory(\BookStack\User::class)->make(); |
| 10 | 10 | ||
| 11 | $this->setSettings(['registration-enabled' => 'true']); | 11 | $this->setSettings(['registration-enabled' => 'true']); |
| 12 | - $this->setEnvironment(['GOOGLE_APP_ID' => 'abc123', 'GOOGLE_APP_SECRET' => '123abc', 'APP_URL' => 'http://localhost']); | 12 | + config(['GOOGLE_APP_ID' => 'abc123', 'GOOGLE_APP_SECRET' => '123abc', 'APP_URL' => 'http://localhost']); |
| 13 | 13 | ||
| 14 | $mockSocialite = Mockery::mock('Laravel\Socialite\Contracts\Factory'); | 14 | $mockSocialite = Mockery::mock('Laravel\Socialite\Contracts\Factory'); |
| 15 | $this->app['Laravel\Socialite\Contracts\Factory'] = $mockSocialite; | 15 | $this->app['Laravel\Socialite\Contracts\Factory'] = $mockSocialite; |
| ... | @@ -32,11 +32,4 @@ class SocialAuthTest extends TestCase | ... | @@ -32,11 +32,4 @@ class SocialAuthTest extends TestCase |
| 32 | $this->seeInDatabase('social_accounts', ['user_id' => $user->id]); | 32 | $this->seeInDatabase('social_accounts', ['user_id' => $user->id]); |
| 33 | } | 33 | } |
| 34 | 34 | ||
| 35 | - protected function setEnvironment($array) | ||
| 36 | - { | ||
| 37 | - foreach ($array as $key => $value) { | ||
| 38 | - putenv("$key=$value"); | ||
| 39 | - } | ||
| 40 | - } | ||
| 41 | - | ||
| 42 | } | 35 | } | ... | ... |
| ... | @@ -5,7 +5,7 @@ use Illuminate\Support\Facades\DB; | ... | @@ -5,7 +5,7 @@ use Illuminate\Support\Facades\DB; |
| 5 | class EntityTest extends TestCase | 5 | class EntityTest extends TestCase |
| 6 | { | 6 | { |
| 7 | 7 | ||
| 8 | - public function testEntityCreation() | 8 | + public function test_entity_creation() |
| 9 | { | 9 | { |
| 10 | 10 | ||
| 11 | // Test Creation | 11 | // Test Creation |
| ... | @@ -51,7 +51,7 @@ class EntityTest extends TestCase | ... | @@ -51,7 +51,7 @@ class EntityTest extends TestCase |
| 51 | return \BookStack\Book::find($book->id); | 51 | return \BookStack\Book::find($book->id); |
| 52 | } | 52 | } |
| 53 | 53 | ||
| 54 | - public function testBookSortPageShows() | 54 | + public function test_book_sort_page_shows() |
| 55 | { | 55 | { |
| 56 | $books = \BookStack\Book::all(); | 56 | $books = \BookStack\Book::all(); |
| 57 | $bookToSort = $books[0]; | 57 | $bookToSort = $books[0]; |
| ... | @@ -65,7 +65,7 @@ class EntityTest extends TestCase | ... | @@ -65,7 +65,7 @@ class EntityTest extends TestCase |
| 65 | ->see($books[1]->name); | 65 | ->see($books[1]->name); |
| 66 | } | 66 | } |
| 67 | 67 | ||
| 68 | - public function testBookSortItemReturnsBookContent() | 68 | + public function test_book_sort_item_returns_book_content() |
| 69 | { | 69 | { |
| 70 | $books = \BookStack\Book::all(); | 70 | $books = \BookStack\Book::all(); |
| 71 | $bookToSort = $books[0]; | 71 | $bookToSort = $books[0]; |
| ... | @@ -155,7 +155,7 @@ class EntityTest extends TestCase | ... | @@ -155,7 +155,7 @@ class EntityTest extends TestCase |
| 155 | return $book; | 155 | return $book; |
| 156 | } | 156 | } |
| 157 | 157 | ||
| 158 | - public function testPageSearch() | 158 | + public function test_page_search() |
| 159 | { | 159 | { |
| 160 | $book = \BookStack\Book::all()->first(); | 160 | $book = \BookStack\Book::all()->first(); |
| 161 | $page = $book->pages->first(); | 161 | $page = $book->pages->first(); |
| ... | @@ -170,7 +170,7 @@ class EntityTest extends TestCase | ... | @@ -170,7 +170,7 @@ class EntityTest extends TestCase |
| 170 | ->seePageIs($page->getUrl()); | 170 | ->seePageIs($page->getUrl()); |
| 171 | } | 171 | } |
| 172 | 172 | ||
| 173 | - public function testInvalidPageSearch() | 173 | + public function test_invalid_page_search() |
| 174 | { | 174 | { |
| 175 | $this->asAdmin() | 175 | $this->asAdmin() |
| 176 | ->visit('/') | 176 | ->visit('/') |
| ... | @@ -180,7 +180,7 @@ class EntityTest extends TestCase | ... | @@ -180,7 +180,7 @@ class EntityTest extends TestCase |
| 180 | ->seeStatusCode(200); | 180 | ->seeStatusCode(200); |
| 181 | } | 181 | } |
| 182 | 182 | ||
| 183 | - public function testEmptySearchRedirectsBack() | 183 | + public function test_empty_search_redirects_back() |
| 184 | { | 184 | { |
| 185 | $this->asAdmin() | 185 | $this->asAdmin() |
| 186 | ->visit('/') | 186 | ->visit('/') |
| ... | @@ -188,7 +188,7 @@ class EntityTest extends TestCase | ... | @@ -188,7 +188,7 @@ class EntityTest extends TestCase |
| 188 | ->seePageIs('/'); | 188 | ->seePageIs('/'); |
| 189 | } | 189 | } |
| 190 | 190 | ||
| 191 | - public function testBookSearch() | 191 | + public function test_book_search() |
| 192 | { | 192 | { |
| 193 | $book = \BookStack\Book::all()->first(); | 193 | $book = \BookStack\Book::all()->first(); |
| 194 | $page = $book->pages->last(); | 194 | $page = $book->pages->last(); |
| ... | @@ -202,7 +202,7 @@ class EntityTest extends TestCase | ... | @@ -202,7 +202,7 @@ class EntityTest extends TestCase |
| 202 | ->see($chapter->name); | 202 | ->see($chapter->name); |
| 203 | } | 203 | } |
| 204 | 204 | ||
| 205 | - public function testEmptyBookSearchRedirectsBack() | 205 | + public function test_empty_book_search_redirects_back() |
| 206 | { | 206 | { |
| 207 | $book = \BookStack\Book::all()->first(); | 207 | $book = \BookStack\Book::all()->first(); |
| 208 | $this->asAdmin() | 208 | $this->asAdmin() |
| ... | @@ -212,7 +212,7 @@ class EntityTest extends TestCase | ... | @@ -212,7 +212,7 @@ class EntityTest extends TestCase |
| 212 | } | 212 | } |
| 213 | 213 | ||
| 214 | 214 | ||
| 215 | - public function testEntitiesViewableAfterCreatorDeletion() | 215 | + public function test_entities_viewable_after_creator_deletion() |
| 216 | { | 216 | { |
| 217 | // Create required assets and revisions | 217 | // Create required assets and revisions |
| 218 | $creator = $this->getNewUser(); | 218 | $creator = $this->getNewUser(); |
| ... | @@ -225,7 +225,7 @@ class EntityTest extends TestCase | ... | @@ -225,7 +225,7 @@ class EntityTest extends TestCase |
| 225 | $this->checkEntitiesViewable($entities); | 225 | $this->checkEntitiesViewable($entities); |
| 226 | } | 226 | } |
| 227 | 227 | ||
| 228 | - public function testEntitiesViewableAfterUpdaterDeletion() | 228 | + public function test_entities_viewable_after_updater_deletion() |
| 229 | { | 229 | { |
| 230 | // Create required assets and revisions | 230 | // Create required assets and revisions |
| 231 | $creator = $this->getNewUser(); | 231 | $creator = $this->getNewUser(); | ... | ... |
| ... | @@ -3,7 +3,7 @@ | ... | @@ -3,7 +3,7 @@ |
| 3 | class PublicViewTest extends TestCase | 3 | class PublicViewTest extends TestCase |
| 4 | { | 4 | { |
| 5 | 5 | ||
| 6 | - public function testBooksViewable() | 6 | + public function test_books_viewable() |
| 7 | { | 7 | { |
| 8 | $this->setSettings(['app-public' => 'true']); | 8 | $this->setSettings(['app-public' => 'true']); |
| 9 | $books = \BookStack\Book::orderBy('name', 'asc')->take(10)->get(); | 9 | $books = \BookStack\Book::orderBy('name', 'asc')->take(10)->get(); |
| ... | @@ -13,14 +13,14 @@ class PublicViewTest extends TestCase | ... | @@ -13,14 +13,14 @@ class PublicViewTest extends TestCase |
| 13 | $this->visit('/books') | 13 | $this->visit('/books') |
| 14 | ->seeStatusCode(200) | 14 | ->seeStatusCode(200) |
| 15 | ->see($books[0]->name) | 15 | ->see($books[0]->name) |
| 16 | - // Check indavidual book page is showing and it's child contents are visible. | 16 | + // Check individual book page is showing and it's child contents are visible. |
| 17 | ->click($bookToVisit->name) | 17 | ->click($bookToVisit->name) |
| 18 | ->seePageIs($bookToVisit->getUrl()) | 18 | ->seePageIs($bookToVisit->getUrl()) |
| 19 | ->see($bookToVisit->name) | 19 | ->see($bookToVisit->name) |
| 20 | ->see($bookToVisit->chapters()->first()->name); | 20 | ->see($bookToVisit->chapters()->first()->name); |
| 21 | } | 21 | } |
| 22 | 22 | ||
| 23 | - public function testChaptersViewable() | 23 | + public function test_chapters_viewable() |
| 24 | { | 24 | { |
| 25 | $this->setSettings(['app-public' => 'true']); | 25 | $this->setSettings(['app-public' => 'true']); |
| 26 | $chapterToVisit = \BookStack\Chapter::first(); | 26 | $chapterToVisit = \BookStack\Chapter::first(); |
| ... | @@ -30,7 +30,7 @@ class PublicViewTest extends TestCase | ... | @@ -30,7 +30,7 @@ class PublicViewTest extends TestCase |
| 30 | $this->visit($chapterToVisit->getUrl()) | 30 | $this->visit($chapterToVisit->getUrl()) |
| 31 | ->seeStatusCode(200) | 31 | ->seeStatusCode(200) |
| 32 | ->see($chapterToVisit->name) | 32 | ->see($chapterToVisit->name) |
| 33 | - // Check indavidual chapter page is showing and it's child contents are visible. | 33 | + // Check individual chapter page is showing and it's child contents are visible. |
| 34 | ->see($pageToVisit->name) | 34 | ->see($pageToVisit->name) |
| 35 | ->click($pageToVisit->name) | 35 | ->click($pageToVisit->name) |
| 36 | ->see($chapterToVisit->book->name) | 36 | ->see($chapterToVisit->book->name) | ... | ... |
-
Please register or sign in to post a comment