diff options
20 files changed, 329 insertions, 36 deletions
diff --git a/config/app.php b/config/app.php index efe745e8..214acb1c 100644 --- a/config/app.php +++ b/config/app.php @@ -20,6 +20,7 @@ return [ \Engelsystem\Helpers\TranslationServiceProvider::class, \Engelsystem\Http\ResponseServiceProvider::class, \Engelsystem\Http\Psr7ServiceProvider::class, + \Engelsystem\Helpers\AuthenticatorServiceProvider::class, \Engelsystem\Renderer\TwigServiceProvider::class, \Engelsystem\Middleware\RouteDispatcherServiceProvider::class, \Engelsystem\Middleware\RequestHandlerServiceProvider::class, @@ -30,7 +31,6 @@ return [ // Application middleware 'middleware' => [ - // Basic initialization \Engelsystem\Middleware\SendResponseHandler::class, \Engelsystem\Middleware\ExceptionHandler::class, @@ -41,6 +41,8 @@ return [ // The application code \Engelsystem\Middleware\ErrorHandler::class, \Engelsystem\Middleware\RouteDispatcher::class, + + // Handle request \Engelsystem\Middleware\RequestHandler::class, ], ]; diff --git a/includes/helper/email_helper.php b/includes/helper/email_helper.php index e9e5a323..7cd60933 100644 --- a/includes/helper/email_helper.php +++ b/includes/helper/email_helper.php @@ -28,7 +28,7 @@ function engelsystem_email_to_user($recipient_user, $title, $message, $not_if_it $recipient_user['email'], $title, 'emails/mail', - ['user' => $recipient_user['Nick'], 'message' => $message] + ['username' => $recipient_user['Nick'], 'message' => $message] ); $translator->setLocale($locale); diff --git a/includes/view/User_view.php b/includes/view/User_view.php index f8160765..1b3c0daf 100644 --- a/includes/view/User_view.php +++ b/includes/view/User_view.php @@ -1,6 +1,7 @@ <?php use Carbon\Carbon; +use Engelsystem\Models\User\User; /** * Renders user settings page @@ -279,6 +280,14 @@ function Users_table_header_link($column, $label, $order_by) */ function User_shift_state_render($user) { + if ($user instanceof User) { + $userModel = $user; + $user = [ + 'Gekommen' => $userModel->state->arrived, + 'UID' => $user->id, + ]; + } + if (!$user['Gekommen']) { return ''; } diff --git a/resources/views/emails/mail.twig b/resources/views/emails/mail.twig index c99fc104..ec70f594 100644 --- a/resources/views/emails/mail.twig +++ b/resources/views/emails/mail.twig @@ -1,4 +1,4 @@ -{{ __('Hi %s,', [user]) }} +{{ __('Hi %s,', [username]) }} {{ __('here is a message for you from the %s:', [config('app_name')]) }} {{ message|raw }} diff --git a/resources/views/layouts/app.twig b/resources/views/layouts/app.twig index ed0d96af..fcbcc665 100644 --- a/resources/views/layouts/app.twig +++ b/resources/views/layouts/app.twig @@ -1,4 +1,4 @@ -{% set theme = user.color|default(config('theme')) %} +{% set theme = user.settings.theme|default(config('theme')) %} <!DOCTYPE html> <html lang="{{ session_get('locale')|split('_')[0]|escape('html_attr') }}"> <head> diff --git a/resources/views/layouts/parts/navbar.twig b/resources/views/layouts/parts/navbar.twig index 22797a7a..61c6a10b 100644 --- a/resources/views/layouts/parts/navbar.twig +++ b/resources/views/layouts/parts/navbar.twig @@ -21,7 +21,8 @@ <span class="icon-bar"></span> </button> <a class="navbar-brand" href="{{ url('/') }}"> - <span class="icon-icon_angel"></span> <strong class="visible-lg-inline">{{ config('app_name')|upper }}</strong> + <span class="icon-icon_angel"></span> + <strong class="visible-lg-inline">{{ config('app_name')|upper }}</strong> </a> </div> @@ -51,7 +52,7 @@ {{ menuUserHints() }} {% if has_permission_to('user_myshifts') %} - {{ elements.toolbar_item(user.Nick, url('users', {'action': 'view'}), 'users', 'icon icon-icon_angel') }} + {{ elements.toolbar_item(user.name, url('users', {'action': 'view'}), 'users', 'icon icon-icon_angel') }} {% endif %} {% if has_permission_to('user_settings') or has_permission_to('logout') %} diff --git a/src/Exceptions/Handlers/Whoops.php b/src/Exceptions/Handlers/Whoops.php index 630aca1d..72e97776 100644 --- a/src/Exceptions/Handlers/Whoops.php +++ b/src/Exceptions/Handlers/Whoops.php @@ -4,6 +4,7 @@ namespace Engelsystem\Exceptions\Handlers; use Engelsystem\Application; use Engelsystem\Container\Container; +use Engelsystem\Helpers\Authenticator; use Engelsystem\Http\Request; use Throwable; use Whoops\Handler\JsonResponseHandler; @@ -81,9 +82,15 @@ class Whoops extends Legacy implements HandlerInterface */ protected function getData() { - global $user; - $data = []; + $user = null; + + if ($this->app->has('authenticator')) { + /** @var Authenticator $authenticator */ + $authenticator = $this->app->get('authenticator'); + $user = $authenticator->user(); + } + $data['user'] = $user; $data['Booted'] = $this->app->isBooted(); diff --git a/src/Helpers/Authenticator.php b/src/Helpers/Authenticator.php new file mode 100644 index 00000000..eee7b965 --- /dev/null +++ b/src/Helpers/Authenticator.php @@ -0,0 +1,56 @@ +<?php + +namespace Engelsystem\Helpers; + +use Engelsystem\Models\BaseModel; +use Engelsystem\Models\User\User; +use Engelsystem\Models\User\User as UserRepository; +use Symfony\Component\HttpFoundation\Session\Session; + +class Authenticator +{ + /** @var UserRepository */ + protected $user = null; + + /** @var Session */ + protected $session; + + /** @var BaseModel */ + protected $userRepository; + + /** + * @param Session $session + * @param UserRepository $userRepository + */ + public function __construct(Session $session, UserRepository $userRepository) + { + $this->session = $session; + $this->userRepository = $userRepository; + } + + /** + * @return User|null + */ + public function user() + { + if ($this->user) { + return $this->user; + } + + $userId = $this->session->get('uid'); + if (!$userId) { + return null; + } + + $user = $this + ->userRepository + ->find($userId); + if (!$user) { + return null; + } + + $this->user = $user; + + return $user; + } +} diff --git a/src/Helpers/AuthenticatorServiceProvider.php b/src/Helpers/AuthenticatorServiceProvider.php new file mode 100644 index 00000000..b7508b01 --- /dev/null +++ b/src/Helpers/AuthenticatorServiceProvider.php @@ -0,0 +1,17 @@ +<?php + +namespace Engelsystem\Helpers; + +use Engelsystem\Container\ServiceProvider; + +class AuthenticatorServiceProvider extends ServiceProvider +{ + public function register() + { + /** @var Authenticator $authenticator */ + $authenticator = $this->app->make(Authenticator::class); + + $this->app->instance(Authenticator::class, $authenticator); + $this->app->instance('authenticator', $authenticator); + } +} diff --git a/src/Middleware/LegacyMiddleware.php b/src/Middleware/LegacyMiddleware.php index 4f61ad32..ce1eadef 100644 --- a/src/Middleware/LegacyMiddleware.php +++ b/src/Middleware/LegacyMiddleware.php @@ -2,6 +2,7 @@ namespace Engelsystem\Middleware; +use Engelsystem\Helpers\Authenticator; use Engelsystem\Helpers\Translator; use Engelsystem\Http\Request; use Engelsystem\Http\Response; @@ -35,12 +36,17 @@ class LegacyMiddleware implements MiddlewareInterface /** @var ContainerInterface */ protected $container; + /** @var Authenticator */ + protected $auth; + /** * @param ContainerInterface $container + * @param Authenticator $auth */ - public function __construct(ContainerInterface $container) + public function __construct(ContainerInterface $container, Authenticator $auth) { $this->container = $container; + $this->auth = $auth; } /** @@ -56,7 +62,6 @@ class LegacyMiddleware implements MiddlewareInterface ServerRequestInterface $request, RequestHandlerInterface $handler ): ResponseInterface { - global $user; global $privileges; global $page; @@ -68,7 +73,7 @@ class LegacyMiddleware implements MiddlewareInterface $page = str_replace('-', '_', $page); } if ($page == '/') { - $page = isset($user) ? 'news' : 'login'; + $page = $this->auth->user() ? 'news' : 'login'; } $title = $content = ''; diff --git a/src/Models/BaseModel.php b/src/Models/BaseModel.php index 2367f5b6..49255905 100644 --- a/src/Models/BaseModel.php +++ b/src/Models/BaseModel.php @@ -30,7 +30,7 @@ abstract class BaseModel extends Model * * @param mixed $id * @param array $columns - * @return Builder|Builder[]|Collection|Model|null + * @return Builder|Builder[]|Collection|static|null */ public static function find($id, $columns = ['*']) { diff --git a/src/Renderer/Twig/Extensions/Authentication.php b/src/Renderer/Twig/Extensions/Authentication.php index 6a72d825..20ede828 100644 --- a/src/Renderer/Twig/Extensions/Authentication.php +++ b/src/Renderer/Twig/Extensions/Authentication.php @@ -2,11 +2,23 @@ namespace Engelsystem\Renderer\Twig\Extensions; +use Engelsystem\Helpers\Authenticator; use Twig_Extension as TwigExtension; use Twig_Function as TwigFunction; class Authentication extends TwigExtension { + /** @var Authenticator */ + protected $auth; + + /** + * @param Authenticator $auth + */ + public function __construct(Authenticator $auth) + { + $this->auth = $auth; + } + /** * @return TwigFunction[] */ @@ -19,18 +31,26 @@ class Authentication extends TwigExtension ]; } + /** + * @return bool + */ public function isAuthenticated() { - global $user; - - return !empty($user); + return (bool)$this->auth->user(); } + /** + * @return bool + */ public function isGuest() { return !$this->isAuthenticated(); } + /** + * @param $privilege + * @return bool + */ public function checkAuth($privilege) { global $privileges; diff --git a/src/Renderer/Twig/Extensions/Globals.php b/src/Renderer/Twig/Extensions/Globals.php index f9bffbc8..ef29a819 100644 --- a/src/Renderer/Twig/Extensions/Globals.php +++ b/src/Renderer/Twig/Extensions/Globals.php @@ -2,11 +2,23 @@ namespace Engelsystem\Renderer\Twig\Extensions; +use Engelsystem\Helpers\Authenticator; use Twig_Extension as TwigExtension; use Twig_Extension_GlobalsInterface as GlobalsInterface; class Globals extends TwigExtension implements GlobalsInterface { + /** @var Authenticator */ + protected $auth; + + /** + * @param Authenticator $auth + */ + public function __construct(Authenticator $auth) + { + $this->auth = $auth; + } + /** * Returns a list of global variables to add to the existing list. * @@ -14,10 +26,10 @@ class Globals extends TwigExtension implements GlobalsInterface */ public function getGlobals() { - global $user; + $user = $this->auth->user(); return [ - 'user' => isset($user) ? $user : [], + 'user' => $user ? $user : [], ]; } } diff --git a/tests/Unit/Exceptions/Handlers/WhoopsTest.php b/tests/Unit/Exceptions/Handlers/WhoopsTest.php index 4062979b..4949c45a 100644 --- a/tests/Unit/Exceptions/Handlers/WhoopsTest.php +++ b/tests/Unit/Exceptions/Handlers/WhoopsTest.php @@ -5,6 +5,7 @@ namespace Engelsystem\Test\Unit\Exceptions\handlers; use Engelsystem\Application; use Engelsystem\Exceptions\Handlers\Whoops; +use Engelsystem\Helpers\Authenticator; use Engelsystem\Http\Request; use Exception; use PHPUnit\Framework\TestCase; @@ -23,15 +24,23 @@ class WhoopsTest extends TestCase { /** @var Application|Mock $app */ $app = $this->createMock(Application::class); + /** @var Authenticator|Mock $auth */ + $auth = $this->createMock(Authenticator::class); /** @var Request|Mock $request */ $request = $this->createMock(Request::class); - $request->expects($this->once()) - ->method('isXmlHttpRequest') - ->willReturn(true); /** @var WhoopsRunnerInterface|Mock $whoopsRunner */ $whoopsRunner = $this->getMockForAbstractClass(WhoopsRunnerInterface::class); /** @var PrettyPageHandler|Mock $prettyPageHandler */ $prettyPageHandler = $this->createMock(PrettyPageHandler::class); + /** @var JsonResponseHandler|Mock $jsonResponseHandler */ + $jsonResponseHandler = $this->createMock(JsonResponseHandler::class); + /** @var Exception|Mock $exception */ + $exception = $this->createMock(Exception::class); + + $request->expects($this->once()) + ->method('isXmlHttpRequest') + ->willReturn(true); + $prettyPageHandler ->expects($this->atLeastOnce()) ->method('setApplicationPaths'); @@ -41,16 +50,13 @@ class WhoopsTest extends TestCase $prettyPageHandler ->expects($this->once()) ->method('addDataTable'); - /** @var JsonResponseHandler|Mock $jsonResponseHandler */ - $jsonResponseHandler = $this->createMock(JsonResponseHandler::class); + $jsonResponseHandler->expects($this->once()) ->method('setJsonApi') ->with(true); $jsonResponseHandler->expects($this->once()) ->method('addTraceToOutput') ->with(true); - /** @var Exception|Mock $exception */ - $exception = $this->createMock(Exception::class); $app->expects($this->exactly(3)) ->method('make') @@ -64,6 +70,18 @@ class WhoopsTest extends TestCase $prettyPageHandler, $jsonResponseHandler ); + $app->expects($this->once()) + ->method('has') + ->with('authenticator') + ->willReturn(true); + $app->expects($this->once()) + ->method('get') + ->with('authenticator') + ->willReturn($auth); + + $auth->expects($this->once()) + ->method('user') + ->willReturn(null); $whoopsRunner ->expects($this->exactly(2)) diff --git a/tests/Unit/Helpers/AuthenticatorServiceProviderTest.php b/tests/Unit/Helpers/AuthenticatorServiceProviderTest.php new file mode 100644 index 00000000..f7819da2 --- /dev/null +++ b/tests/Unit/Helpers/AuthenticatorServiceProviderTest.php @@ -0,0 +1,25 @@ +<?php + +namespace Engelsystem\Test\Unit\Helpers; + +use Engelsystem\Application; +use Engelsystem\Helpers\Authenticator; +use Engelsystem\Helpers\AuthenticatorServiceProvider; +use Engelsystem\Test\Unit\ServiceProviderTest; + +class AuthenticatorServiceProviderTest extends ServiceProviderTest +{ + /** + * @covers \Engelsystem\Helpers\AuthenticatorServiceProvider::register() + */ + public function testRegister() + { + $app = new Application(); + + $serviceProvider = new AuthenticatorServiceProvider($app); + $serviceProvider->register(); + + $this->assertInstanceOf(Authenticator::class, $app->get(Authenticator::class)); + $this->assertInstanceOf(Authenticator::class, $app->get('authenticator')); + } +} diff --git a/tests/Unit/Helpers/AuthenticatorTest.php b/tests/Unit/Helpers/AuthenticatorTest.php new file mode 100644 index 00000000..085887c4 --- /dev/null +++ b/tests/Unit/Helpers/AuthenticatorTest.php @@ -0,0 +1,55 @@ +<?php + +namespace Engelsystem\Test\Unit\Helpers; + +use Engelsystem\Helpers\Authenticator; +use Engelsystem\Models\User\User; +use Engelsystem\Test\Unit\Helpers\Stub\UserModelImplementation; +use Engelsystem\Test\Unit\ServiceProviderTest; +use PHPUnit\Framework\MockObject\MockObject; +use Symfony\Component\HttpFoundation\Session\Session; + +class AuthenticatorTest extends ServiceProviderTest +{ + /** + * @covers \Engelsystem\Helpers\Authenticator::__construct( + * @covers \Engelsystem\Helpers\Authenticator::user + */ + public function testUser() + { + /** @var Session|MockObject $session */ + $session = $this->createMock(Session::class); + /** @var UserModelImplementation|MockObject $userRepository */ + $userRepository = new UserModelImplementation(); + /** @var User|MockObject $user */ + $user = $this->createMock(User::class); + + $session->expects($this->exactly(3)) + ->method('get') + ->with('uid') + ->willReturnOnConsecutiveCalls( + null, + 42, + 1337 + ); + + $auth = new Authenticator($session, $userRepository); + + // Not in session + $this->assertEquals(null, $auth->user()); + + // Unknown user + UserModelImplementation::$id = 42; + $this->assertEquals(null, $auth->user()); + + // User found + UserModelImplementation::$id = 1337; + UserModelImplementation::$user = $user; + $this->assertEquals($user, $auth->user()); + + // User cached + UserModelImplementation::$id = null; + UserModelImplementation::$user = null; + $this->assertEquals($user, $auth->user()); + } +} diff --git a/tests/Unit/Helpers/Stub/UserModelImplementation.php b/tests/Unit/Helpers/Stub/UserModelImplementation.php new file mode 100644 index 00000000..934aaeb2 --- /dev/null +++ b/tests/Unit/Helpers/Stub/UserModelImplementation.php @@ -0,0 +1,29 @@ +<?php + +namespace Engelsystem\Test\Unit\Helpers\Stub; + +use Engelsystem\Models\User\User; +use InvalidArgumentException; + +class UserModelImplementation extends User +{ + /** @var User */ + public static $user = null; + + /** @var int */ + public static $id = null; + + /** + * @param mixed $id + * @param array $columns + * @return User|null + */ + public static function find($id, $columns = ['*']) + { + if ($id != static::$id) { + throw new InvalidArgumentException('Wrong user ID searched'); + } + + return self::$user; + } +} diff --git a/tests/Unit/Middleware/LegacyMiddlewareTest.php b/tests/Unit/Middleware/LegacyMiddlewareTest.php index 09674a59..caea483d 100644 --- a/tests/Unit/Middleware/LegacyMiddlewareTest.php +++ b/tests/Unit/Middleware/LegacyMiddlewareTest.php @@ -2,6 +2,7 @@ namespace Engelsystem\Test\Unit\Middleware; +use Engelsystem\Helpers\Authenticator; use Engelsystem\Helpers\Translator; use Engelsystem\Http\Request; use Engelsystem\Middleware\LegacyMiddleware; @@ -23,9 +24,11 @@ class LegacyMiddlewareTest extends TestCase { /** @var ContainerInterface|MockObject $container */ $container = $this->getMockForAbstractClass(ContainerInterface::class); + /** @var Authenticator|MockObject $auth */ + $auth = $this->createMock(Authenticator::class); /** @var LegacyMiddleware|MockObject $middleware */ $middleware = $this->getMockBuilder(LegacyMiddleware::class) - ->setConstructorArgs([$container]) + ->setConstructorArgs([$container, $auth]) ->setMethods(['loadPage', 'renderPage']) ->getMock(); /** @var Request|MockObject $defaultRequest */ @@ -70,6 +73,10 @@ class LegacyMiddlewareTest extends TestCase $defaultRequest ); + $auth->expects($this->atLeastOnce()) + ->method('user') + ->willReturn(false); + $translator->expects($this->exactly(2)) ->method('translate') ->willReturnOnConsecutiveCalls( diff --git a/tests/Unit/Renderer/Twig/Extensions/AuthenticationTest.php b/tests/Unit/Renderer/Twig/Extensions/AuthenticationTest.php index 0a72c0e7..b67d4eed 100644 --- a/tests/Unit/Renderer/Twig/Extensions/AuthenticationTest.php +++ b/tests/Unit/Renderer/Twig/Extensions/AuthenticationTest.php @@ -2,16 +2,23 @@ namespace Engelsystem\Test\Unit\Renderer\Twig\Extensions; +use Engelsystem\Helpers\Authenticator; +use Engelsystem\Models\User\User; use Engelsystem\Renderer\Twig\Extensions\Authentication; +use PHPUnit\Framework\MockObject\MockObject; class AuthenticationTest extends ExtensionTest { /** + * @covers \Engelsystem\Renderer\Twig\Extensions\Authentication::__construct * @covers \Engelsystem\Renderer\Twig\Extensions\Authentication::getFunctions */ public function testGetFunctions() { - $extension = new Authentication(); + /** @var Authenticator|MockObject $auth */ + $auth = $this->createMock(Authenticator::class); + + $extension = new Authentication($auth); $functions = $extension->getFunctions(); $this->assertExtensionExists('is_user', [$extension, 'isAuthenticated'], $functions); @@ -25,15 +32,24 @@ class AuthenticationTest extends ExtensionTest */ public function testIsAuthenticated() { - global $user; - $user = []; + /** @var Authenticator|MockObject $auth */ + $auth = $this->createMock(Authenticator::class); + $user = new User(); + + $auth->expects($this->exactly(4)) + ->method('user') + ->willReturnOnConsecutiveCalls( + null, + null, + $user, + $user + ); - $extension = new Authentication(); + $extension = new Authentication($auth); $this->assertFalse($extension->isAuthenticated()); $this->assertTrue($extension->isGuest()); - $user = ['lorem' => 'ipsum']; $this->assertTrue($extension->isAuthenticated()); $this->assertFalse($extension->isGuest()); } @@ -46,7 +62,10 @@ class AuthenticationTest extends ExtensionTest global $privileges; $privileges = []; - $extension = new Authentication(); + /** @var Authenticator|MockObject $auth */ + $auth = $this->createMock(Authenticator::class); + + $extension = new Authentication($auth); $this->assertFalse($extension->checkAuth('foo.bar')); diff --git a/tests/Unit/Renderer/Twig/Extensions/GlobalsTest.php b/tests/Unit/Renderer/Twig/Extensions/GlobalsTest.php index 6441904b..a317ad97 100644 --- a/tests/Unit/Renderer/Twig/Extensions/GlobalsTest.php +++ b/tests/Unit/Renderer/Twig/Extensions/GlobalsTest.php @@ -2,25 +2,36 @@ namespace Engelsystem\Test\Unit\Renderer\Twig\Extensions; +use Engelsystem\Helpers\Authenticator; +use Engelsystem\Models\User\User; use Engelsystem\Renderer\Twig\Extensions\Globals; +use PHPUnit\Framework\MockObject\MockObject; class GlobalsTest extends ExtensionTest { /** + * @covers \Engelsystem\Renderer\Twig\Extensions\Globals::__construct * @covers \Engelsystem\Renderer\Twig\Extensions\Globals::getGlobals */ public function testGetGlobals() { - global $user; - $user = []; + /** @var Authenticator|MockObject $auth */ + $auth = $this->createMock(Authenticator::class); + $user = new User(); - $extension = new Globals(); + $auth->expects($this->exactly(2)) + ->method('user') + ->willReturnOnConsecutiveCalls( + null, + $user + ); + + $extension = new Globals($auth); $globals = $extension->getGlobals(); $this->assertGlobalsExists('user', [], $globals); - $user['foo'] = 'bar'; $globals = $extension->getGlobals(); - $this->assertGlobalsExists('user', ['foo' => 'bar'], $globals); + $this->assertGlobalsExists('user', $user, $globals); } } |