From 6cfe0ded85f1111dfe40db256df5fe66818af404 Mon Sep 17 00:00:00 2001
From: Wojtek
Date: Sun, 27 Feb 2022 01:53:35 +0100
Subject: [PATCH] Introduces breaking changes Changes directory structure in
src Renames parameters in config Removes built in User providers Adds
translations to errors Removes docker and public files
---
README.md | 41 +++-----
config/config.php | 2 +-
config/parameters.yaml | 7 +-
data/users | Bin 922 -> 0 bytes
docker/nginx/kavalanche-security.conf | 44 ---------
docker/php-fpm/dockerfile | 9 --
public/index.php | 92 ------------------
src/AuthenticationProvider.php | 46 ---------
src/DatabaseUserProvider.php | 40 --------
src/{ => Exception}/SecurityException.php | 2 +-
.../UserNotAuthenticatedException.php | 2 +-
src/InMemoryUserProvider.php | 30 ------
src/{ => Interface}/UserInterface.php | 0
src/{ => Interface}/UserProviderInterface.php | 0
src/Provider/AuthenticationProvider.php | 55 +++++++++++
src/User.php | 76 ---------------
tests/InMemoryUserProviderTest.php | 21 ----
tests/data/users | Bin 889 -> 0 bytes
translations/en.yaml | 3 +
translations/pl.yaml | 3 +
translations/translations.php | 11 +++
21 files changed, 92 insertions(+), 392 deletions(-)
delete mode 100644 data/users
delete mode 100644 docker/nginx/kavalanche-security.conf
delete mode 100644 docker/php-fpm/dockerfile
delete mode 100644 public/index.php
delete mode 100644 src/AuthenticationProvider.php
delete mode 100644 src/DatabaseUserProvider.php
rename src/{ => Exception}/SecurityException.php (74%)
rename src/{ => Exception}/UserNotAuthenticatedException.php (76%)
delete mode 100644 src/InMemoryUserProvider.php
rename src/{ => Interface}/UserInterface.php (100%)
rename src/{ => Interface}/UserProviderInterface.php (100%)
create mode 100644 src/Provider/AuthenticationProvider.php
delete mode 100644 src/User.php
delete mode 100644 tests/InMemoryUserProviderTest.php
delete mode 100644 tests/data/users
create mode 100644 translations/en.yaml
create mode 100644 translations/pl.yaml
create mode 100644 translations/translations.php
diff --git a/README.md b/README.md
index 2c9ab3f..481cb5a 100644
--- a/README.md
+++ b/README.md
@@ -9,24 +9,12 @@ Simple security library for web applications.
composer require kavalanche/security
```
-2. Create `UserProvider`.
+2. Create `UserProvider`. Refer to [Custom UserProvider](#custom-userprovider) section.
+
+3. Instantiate `AuthenticationProvider` and inject your `UserProvider` into it.
```php
- // $users must be an array of objects implementing Kavalanche\Security\UserInterface
- $userProvider = new Kavalanche\Security\InMemoryUserProvider($users);
- ```
-
- OR
-
- ```php
- // $db is a databse handle (for exapmple PDO instance)
- $userProvider = new Kavalanche\Security\DatabaseUserProvider($db);
- ```
-
-3. Create `AuthenticationProvider` and inject your UserProvider into it.
-
- ```php
- $authenticationProvider = new Kavalanche\Security\AuthenticationProvider($userProvider);
+ $authenticationProvider = new Kavalanche\Security\Provider\AuthenticationProvider($userProvider);
```
4. Check if user is authenticated.
@@ -34,33 +22,33 @@ Simple security library for web applications.
```php
try {
$user = $authenticationProvider->authenticate();
- } catch (Exception $ex) {
+ } catch (Kavalanche\Security\Exception\SecurityException $ex) {
// if you want to allow unauthenticated users, then assign false or null to $user
// if you require user to be authenticated do as follows
- if (!$e instanceof Kavalanche\Security\UserNotAuthenticatedException) {
+ if (!$e instanceof Kavalanche\Security\Exception\UserNotAuthenticatedException) {
// put message in flash session and redirect to user form
// or do whatever your use case demands
}
}
```
- You can specify `redirect_path` in `security.yaml`. Default is `/`.
+ You can specify `redirect-path` in `security.yaml`. Default is `/`.
By default expected login form field names are `email` and `password`.
You can change them by creating a config file named `{app_root}/config/security.yaml` and setting these variables:
- - `login_form_identifier_field` for identifier field
- - `login_form_password_field` for password field
+ - `login-form-identifier-field` for identifier field
+ - `login-form-password-field` for password field
5. Don't forget to put `session_start()` at the beginning of your file.
-# Custom `UserProvider`
+# Custom UserProvider
-If you want to user different UserProvider, you can create your. It must implement `Kavalanche\Security\UserProviderInterface`.
+If you want to user different UserProvider, you can create your. It must implement `Kavalanche\Security\Interface\UserProviderInterface`.
```php
-class UserProvider implements UserProviderInterface {
+class UserProvider implements Kavalanche\Security\Interface\UserProviderInterface {
public function loadUser($identifier) {
@@ -72,7 +60,7 @@ class UserProvider implements UserProviderInterface {
return $user;
}
- throw new Kavalanche\Security\SecurityException('Invalid username.');
+ throw new Kavalanche\Security\Exception\SecurityException('Invalid username.');
}
}
@@ -85,12 +73,9 @@ You can configure identifier type in your `{app_root}/config/security.yaml` file
- It's your obligation to ensure that the usernames are unique.
- You can use this library however you want. To secure the whole application or just some routes.
-- There is a demonstration of use in the public directory of this library. Go ahead and test it.
- You can add multiple roles to each user. Simply assign an array with roles or permissions with `$user->setRoles()` setter.
-- If you want to check that the user has appropriate roles (is authorized), you have to pass $user variable to your checking mechanism.
# To do
- Implement some kind of request abstraction to encapsulate Requests (symfony/http-foundation?)
- Add helper to check permissions
-- Translate exception messages
\ No newline at end of file
diff --git a/config/config.php b/config/config.php
index 7156fad..b30a69c 100644
--- a/config/config.php
+++ b/config/config.php
@@ -2,7 +2,7 @@
namespace Kavalanche\Security;
-function getValueFromConfig(string $key) {
+function config(string $key) {
$config = yaml_parse_file(__DIR__ . '/parameters.yaml');
$userConfigPath = __DIR__ . '/../../../../config/security.yaml';
if (file_exists($userConfigPath)) {
diff --git a/config/parameters.yaml b/config/parameters.yaml
index 79db885..edf6d57 100644
--- a/config/parameters.yaml
+++ b/config/parameters.yaml
@@ -1,4 +1,5 @@
identifier: "email"
-login_form_identifier_field: "email"
-login_form_password_field: "password"
-redirect_path: "/"
\ No newline at end of file
+login-form-identifier-field: "email"
+login-form-password-field: "password"
+redirect-path: "/"
+lang: en
diff --git a/data/users b/data/users
deleted file mode 100644
index 0cf83f74f00fb069b7908f903c44fef06be18f85..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 922
zcmdUsPj7-i5XJp08_$%#qQj+#G$IwOTH6{Ev*Hq0QIT~^)cD=IXb-(e)25dmXYyv|
z&HG_c;Y2~ULQr);K7Pju-b4#B8IXu)bbpvUGm;@N;lx0#1FGE8ROlR86cmerCRW|n
zaj|fN*8~Y=4baPWxhJclC?88PWBE1{N5q&6sHz^Sid>x~Zs?JQ9{bU*=gvFf!qzli
zlSVW1P0?qqG{>v<-P6W5V)?Zh*>0!NbuQPGGLvMDX;R1(aVnRR)qS2y>7}EI&h786
zp9RG`TA7aV&k!!sQZLyyAq-g;
- # fastcgi_param DATABASE_URL "mysql://db_user:db_pass@host:3306/db_name";
-
- # When you are using symlinks to link the document root to the
- # current version of your application, you should pass the real
- # application path instead of the path to the symlink to PHP
- # FPM.
- # Otherwise, PHP's OPcache may not properly detect changes to
- # your PHP files (see https://github.com/zendtech/ZendOptimizerPlus/issues/126
- # for more information).
- fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
- fastcgi_param DOCUMENT_ROOT $realpath_root;
- # Prevents URIs that include the front controller. This will 404:
- # http://domain.tld/index.php/some-path
- # Remove the internal directive to allow URIs like this
- internal;
- }
-
- # return 404 for all other php files not matching the front controller
- # this prevents access to other php files you don't want to be accessible.
- location ~ \.php$ {
- return 404;
- }
-
- error_log /var/log/nginx/kavalanche-security_error.log;
- access_log /var/log/nginx/kavalanche-security_access.log;
-}
diff --git a/docker/php-fpm/dockerfile b/docker/php-fpm/dockerfile
deleted file mode 100644
index c00a89a..0000000
--- a/docker/php-fpm/dockerfile
+++ /dev/null
@@ -1,9 +0,0 @@
-FROM php:7.4-fpm
-
-RUN docker-php-ext-install pdo_mysql
-
-ADD https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions /usr/local/bin/
-RUN chmod +x /usr/local/bin/install-php-extensions && sync && \
- install-php-extensions yaml
-
-RUN cp $PHP_INI_DIR/php.ini-development $PHP_INI_DIR/php.ini
\ No newline at end of file
diff --git a/public/index.php b/public/index.php
deleted file mode 100644
index d4e650a..0000000
--- a/public/index.php
+++ /dev/null
@@ -1,92 +0,0 @@
- 1,
- 'title' => 'Secret note',
- 'body' => 'Secret content'
- ],
- [
- 'user_id' => 2,
- 'title' => 'Hello world',
- 'body' => 'How are you doing?'
- ]
- ];
- $articles = [];
- foreach ($data as $article) {
- if ($article['user_id'] === $user->getId()) {
- $articles[] = (object) $article;
- }
- }
- return $articles;
-}
-
-$users = unserialize(file_get_contents(__DIR__ . '/../data/users'));
-$userProvider = new InMemoryUserProvider($users);
-$authenticationProvider = new AuthenticationProvider($userProvider);
-
-try {
- $user = $authenticationProvider->authenticate();
-} catch (SecurityException $ex) {
- $user = false;
-}
-
-if (isset($_GET['login'])) {
- try {
- $user = $authenticationProvider->authenticate();
- } catch (SecurityException $ex) {
- if (!$ex instanceof UserNotAuthenticatedException) {
- ?>
-
-
-
- getName() ?? "anonymous") . ' | Log out';
-} else {
- echo 'Log in';
-}
-if (!empty($user)) {
- $articles = getUserArticles($user);
-} else {
- $articles = [];
-}
-
-foreach ($articles as $article) {
- ?>
-
- title; ?>
- body; ?>
-
- Log in to see your posts.
';
-}
\ No newline at end of file
diff --git a/src/AuthenticationProvider.php b/src/AuthenticationProvider.php
deleted file mode 100644
index 0c75445..0000000
--- a/src/AuthenticationProvider.php
+++ /dev/null
@@ -1,46 +0,0 @@
-
- */
-class AuthenticationProvider {
-
- private $userProvider;
-
- public function __construct(UserProviderInterface $userProvider) {
- $this->userProvider = $userProvider;
- }
-
- public function authenticate() {
- if (!in_array(getValueFromConfig('identifier'), ['email', 'username'], true)) {
- throw new Exception('User identifier must be one of: email, username');
- }
-
- if (!empty($_SESSION[getValueFromConfig('identifier')])) {
- $user = $this->userProvider->loadUser($_SESSION[getValueFromConfig('identifier')]);
- return $user;
- }
-
- if (!empty($_POST[getValueFromConfig('login_form_identifier_field')])) {
- $user = $this->userProvider->loadUser($_POST[getValueFromConfig('login_form_identifier_field')]);
- if (password_verify($_POST[getValueFromConfig('login_form_password_field')], $user->getPassword())) {
- if (getValueFromConfig('identifier') === 'email') {
- $_SESSION[getValueFromConfig('identifier')] = $user->getEmail();
- } elseif (getValueFromConfig('identifier') === 'username') {
- $_SESSION[getValueFromConfig('identifier')] = $user->getUsername();
- }
- header('Location: ' . getValueFromConfig('redirect_path'));
- exit;
- }
- http_response_code(401);
- throw new SecurityException('Wrong data provided');
- }
- http_response_code(401);
- throw new UserNotAuthenticatedException('Please log in');
- }
-
-}
diff --git a/src/DatabaseUserProvider.php b/src/DatabaseUserProvider.php
deleted file mode 100644
index ac932a7..0000000
--- a/src/DatabaseUserProvider.php
+++ /dev/null
@@ -1,40 +0,0 @@
-
- */
-class DatabaseUserProvider implements UserProviderInterface {
-
- private $db;
-
- public function __construct(PDO $db) {
- $this->db = $db;
- }
-
- public function loadUser(string $identifier): UserInterface {
- if (getValueFromConfig('identifier') === 'email') {
- $sql = "SELECT * FROM user WHERE email = :identifier";
- } elseif (getValueFromConfig('identifier') === 'username') {
- $sql = "SELECT * FROM user WHERE username = :identifier";
- }
- $stmt = $this->db->prepare($sql);
- $stmt->execute([
- 'identifier' => $identifier
- ]);
- $stmt->setFetchMode(PDO::FETCH_CLASS, User::class);
- $user = $stmt->fetch() ?: null;
-
- if (!empty($user) && $user instanceof UserInterface) {
- return $user;
- }
-
- throw new SecurityException('Wrong data provided');
- }
-
-}
diff --git a/src/SecurityException.php b/src/Exception/SecurityException.php
similarity index 74%
rename from src/SecurityException.php
rename to src/Exception/SecurityException.php
index 978c0df..7e54774 100644
--- a/src/SecurityException.php
+++ b/src/Exception/SecurityException.php
@@ -1,6 +1,6 @@
diff --git a/src/UserNotAuthenticatedException.php b/src/Exception/UserNotAuthenticatedException.php
similarity index 76%
rename from src/UserNotAuthenticatedException.php
rename to src/Exception/UserNotAuthenticatedException.php
index 7660372..6b4aa72 100644
--- a/src/UserNotAuthenticatedException.php
+++ b/src/Exception/UserNotAuthenticatedException.php
@@ -1,6 +1,6 @@
diff --git a/src/InMemoryUserProvider.php b/src/InMemoryUserProvider.php
deleted file mode 100644
index 5828ff8..0000000
--- a/src/InMemoryUserProvider.php
+++ /dev/null
@@ -1,30 +0,0 @@
-
- */
-class InMemoryUserProvider implements UserProviderInterface {
-
- private $users;
-
- public function __construct(array $users) {
- $this->users = $users;
- }
-
- public function loadUser(string$identifier): UserInterface {
- foreach ($this->users as $user) {
- if (
- (getValueFromConfig('identifier') === 'email' && $user->getEmail() === $identifier) || (getValueFromConfig('identifier') === 'username' && $user->getUsername() === $identifier) && $user instanceof UserInterface
- ) {
- return $user;
- }
- }
-
- throw new SecurityException('Wrong data provided');
- }
-
-}
diff --git a/src/UserInterface.php b/src/Interface/UserInterface.php
similarity index 100%
rename from src/UserInterface.php
rename to src/Interface/UserInterface.php
diff --git a/src/UserProviderInterface.php b/src/Interface/UserProviderInterface.php
similarity index 100%
rename from src/UserProviderInterface.php
rename to src/Interface/UserProviderInterface.php
diff --git a/src/Provider/AuthenticationProvider.php b/src/Provider/AuthenticationProvider.php
new file mode 100644
index 0000000..fc187a0
--- /dev/null
+++ b/src/Provider/AuthenticationProvider.php
@@ -0,0 +1,55 @@
+
+ */
+class AuthenticationProvider {
+
+ private $userProvider;
+
+ public function __construct(UserProviderInterface $userProvider) {
+ $this->userProvider = $userProvider;
+ }
+
+ public function authenticate() {
+ $identifier = config('identifier');
+ $identifierFormField = config('login-form-identifier-field');
+ $passwordFormField = config('login-form-password-field');
+ if (!in_array($identifier, ['email', 'username'], true)) {
+ throw new SecurityException(_t('security.error.bad-identifier', config('lang')));
+ }
+
+ if (!empty($_SESSION[$identifier])) {
+ $user = $this->userProvider->loadUser($_SESSION[$identifier]);
+ return $user;
+ }
+
+ if (!empty($_POST[$identifierFormField])) {
+ $user = $this->userProvider->loadUser($_POST[$identifierFormField]);
+ if (password_verify($_POST[$passwordFormField], $user->getPassword())) {
+ if ($identifier === 'email') {
+ $_SESSION[$identifier] = $user->getEmail();
+ } elseif ($identifier === 'username') {
+ $_SESSION[$identifier] = $user->getUsername();
+ }
+ header('Location: ' . config('redirect-path'));
+ exit;
+ }
+ http_response_code(401);
+ throw new SecurityException(_t('security.error.bad-input', config('lang')));
+ }
+ http_response_code(401);
+ throw new UserNotAuthenticatedException(_t('security.error.not-logged-in', config('lang')));
+ }
+
+}
diff --git a/src/User.php b/src/User.php
deleted file mode 100644
index 95e03b4..0000000
--- a/src/User.php
+++ /dev/null
@@ -1,76 +0,0 @@
-
- */
-class User implements UserInterface {
-
- private $id;
- private $username;
- private $password;
- private $email;
- private $name;
- private $surname;
- private $roles;
-
- public function getId(): ?int {
- return $this->id;
- }
-
- public function getUsername(): string {
- return $this->username;
- }
-
- public function getPassword(): string {
- return $this->password;
- }
-
- public function getEmail(): string {
- return $this->email;
- }
-
- public function getName(): ?string {
- return $this->name;
- }
-
- public function getSurname(): ?string {
- return $this->surname;
- }
-
- public function getRoles(): array {
- return $this->roles;
- }
-
- public function setId(int $id) {
- $this->id = $id;
- }
-
- public function setUsername(string $username) {
- $this->username = $username;
- }
-
- public function setPassword(string $password) {
- $this->password = $password;
- }
-
- public function setEmail(string $email) {
- $this->email = $email;
- }
-
- public function setName(string $name) {
- $this->name = $name;
- }
-
- public function setSurname(string $surname) {
- $this->surname = $surname;
- }
-
- public function setRoles(array $roles) {
- $this->roles = $roles;
- }
-
-}
diff --git a/tests/InMemoryUserProviderTest.php b/tests/InMemoryUserProviderTest.php
deleted file mode 100644
index 77f86e0..0000000
--- a/tests/InMemoryUserProviderTest.php
+++ /dev/null
@@ -1,21 +0,0 @@
-expectException(Exception::class);
-
- $users = unserialize(file_get_contents(__DIR__ . '/data/users'));
- $userProvider = new \Kavalanche\Security\InMemoryUserProvider($users);
- $userProvider->loadUserByUsername('Kasia');
- }
-
- public function testLoadUserByUsername_returnsUser() {
- $users = unserialize(file_get_contents(__DIR__ . '/data/users'));
- $userProvider = new \Kavalanche\Security\InMemoryUserProvider($users);
- $user = $userProvider->loadUserByUsername('Wojtek');
-
- $this->assertInstanceOf(\Kavalanche\Security\UserInterface::class, $user);
- }
-
-}
diff --git a/tests/data/users b/tests/data/users
deleted file mode 100644
index 3dad48ddd3b614f6fd0fa2657ec9af4efcba07f2..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 889
zcmds!O;5r=5Qh0J8_w8v<-_Tzw4!_!35$G$gsiaGVrlDcx26#OyG!xlr8P00JWle=
zyfgDqFyQ2aW{$wP1=Oc04QROW*lNNylKV-vnhM4dG~pz`XaTAeF%^G{OdoVp09K=l
zIjIJQ^uUlJ8-QlfK4EW{qP#7pLVQGAg{U?SP}Rt)x>l_X2``*IX~jIb6mG`E_)T
zmS*!PU;>#G^s5^Iy84rCx$8l9Le}=kCHF2t=J|Yi_n(