diff --git a/cache/securitylog.txt b/cache/securitylog.txt
new file mode 100644
index 0000000..0123c2b
--- /dev/null
+++ b/cache/securitylog.txt
@@ -0,0 +1,5 @@
+127.0.0.1;2021-09-26 12:01:24;wrong captcha http://localhost/typemill/tm/recoverpw
+127.0.0.1;2021-09-26 12:06:16;wrong captcha http://localhost/typemill/tm/recoverpw
+127.0.0.1;2021-09-27 23:44:57;wrong captcha http://localhost/typemill/tm/recoverpw
+127.0.0.1;2021-09-27 23:51:19;wrong captcha http://localhost/typemill/tm/recoverpw
+127.0.0.1;2021-09-27 23:51:30;wrong captcha http://localhost/typemill/tm/recoverpw
diff --git a/composer.json b/composer.json
index dc2d4b1..bf5529e 100644
--- a/composer.json
+++ b/composer.json
@@ -21,7 +21,8 @@
"jbroadway/urlify": "1.1.3",
"vlucas/valitron": "dev-master",
"laminas/laminas-permissions-acl": "^2.7",
- "akrabat/proxy-detection-middleware": "^0.4.0"
+ "akrabat/proxy-detection-middleware": "^0.4.0",
+ "gregwar/captcha": "1.*"
},
"autoload": {
"psr-4": {
diff --git a/composer.lock b/composer.lock
index d16a63a..0b41d0f 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "7539fdddfa1c0b8d030fa5955b45a928",
+ "content-hash": "bc8436c1ed1a0ea8333205185da466f4",
"packages": [
{
"name": "akrabat/proxy-detection-middleware",
@@ -164,6 +164,63 @@
},
"time": "2019-12-29T11:14:16+00:00"
},
+ {
+ "name": "gregwar/captcha",
+ "version": "v1.1.9",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/Gregwar/Captcha.git",
+ "reference": "4bb668e6b40e3205a020ca5ee4ca8cff8b8780c5"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/Gregwar/Captcha/zipball/4bb668e6b40e3205a020ca5ee4ca8cff8b8780c5",
+ "reference": "4bb668e6b40e3205a020ca5ee4ca8cff8b8780c5",
+ "shasum": ""
+ },
+ "require": {
+ "ext-gd": "*",
+ "ext-mbstring": "*",
+ "php": ">=5.3.0",
+ "symfony/finder": "*"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^6.4"
+ },
+ "type": "captcha",
+ "autoload": {
+ "psr-4": {
+ "Gregwar\\": "src/Gregwar"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Grégoire Passault",
+ "email": "g.passault@gmail.com",
+ "homepage": "http://www.gregwar.com/"
+ },
+ {
+ "name": "Jeremy Livingston",
+ "email": "jeremy.j.livingston@gmail.com"
+ }
+ ],
+ "description": "Captcha generator",
+ "homepage": "https://github.com/Gregwar/Captcha",
+ "keywords": [
+ "bot",
+ "captcha",
+ "spam"
+ ],
+ "support": {
+ "issues": "https://github.com/Gregwar/Captcha/issues",
+ "source": "https://github.com/Gregwar/Captcha/tree/master"
+ },
+ "time": "2020-03-24T14:39:05+00:00"
+ },
{
"name": "jbroadway/urlify",
"version": "1.1.3-stable",
@@ -291,23 +348,23 @@
},
{
"name": "laminas/laminas-zendframework-bridge",
- "version": "1.3.0",
+ "version": "1.4.0",
"source": {
"type": "git",
"url": "https://github.com/laminas/laminas-zendframework-bridge.git",
- "reference": "13af2502d9bb6f7d33be2de4b51fb68c6cdb476e"
+ "reference": "bf180a382393e7db5c1e8d0f2ec0c4af9c724baf"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/laminas/laminas-zendframework-bridge/zipball/13af2502d9bb6f7d33be2de4b51fb68c6cdb476e",
- "reference": "13af2502d9bb6f7d33be2de4b51fb68c6cdb476e",
+ "url": "https://api.github.com/repos/laminas/laminas-zendframework-bridge/zipball/bf180a382393e7db5c1e8d0f2ec0c4af9c724baf",
+ "reference": "bf180a382393e7db5c1e8d0f2ec0c4af9c724baf",
"shasum": ""
},
"require": {
- "php": "^7.3 || ^8.0"
+ "php": "^7.3 || ~8.0.0 || ~8.1.0"
},
"require-dev": {
- "phpunit/phpunit": "^5.7 || ^6.5 || ^7.5 || ^8.1 || ^9.3",
+ "phpunit/phpunit": "^9.3",
"psalm/plugin-phpunit": "^0.15.1",
"squizlabs/php_codesniffer": "^3.5",
"vimeo/psalm": "^4.6"
@@ -349,7 +406,7 @@
"type": "community_bridge"
}
],
- "time": "2021-06-24T12:49:22+00:00"
+ "time": "2021-09-03T17:53:30+00:00"
},
{
"name": "nikic/fast-route",
@@ -919,6 +976,68 @@
],
"time": "2020-10-24T10:57:07+00:00"
},
+ {
+ "name": "symfony/finder",
+ "version": "v5.3.7",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/finder.git",
+ "reference": "a10000ada1e600d109a6c7632e9ac42e8bf2fb93"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/finder/zipball/a10000ada1e600d109a6c7632e9ac42e8bf2fb93",
+ "reference": "a10000ada1e600d109a6c7632e9ac42e8bf2fb93",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2.5",
+ "symfony/polyfill-php80": "^1.16"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Finder\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Finds files and directories via an intuitive fluent interface",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/finder/tree/v5.3.7"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2021-08-04T21:20:46+00:00"
+ },
{
"name": "symfony/polyfill-ctype",
"version": "v1.23.0",
@@ -998,6 +1117,89 @@
],
"time": "2021-02-19T12:13:01+00:00"
},
+ {
+ "name": "symfony/polyfill-php80",
+ "version": "v1.23.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-php80.git",
+ "reference": "1100343ed1a92e3a38f9ae122fc0eb21602547be"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/1100343ed1a92e3a38f9ae122fc0eb21602547be",
+ "reference": "1100343ed1a92e3a38f9ae122fc0eb21602547be",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "1.23-dev"
+ },
+ "thanks": {
+ "name": "symfony/polyfill",
+ "url": "https://github.com/symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Polyfill\\Php80\\": ""
+ },
+ "files": [
+ "bootstrap.php"
+ ],
+ "classmap": [
+ "Resources/stubs"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Ion Bazan",
+ "email": "ion.bazan@gmail.com"
+ },
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-php80/tree/v1.23.1"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2021-07-28T13:41:28+00:00"
+ },
{
"name": "symfony/yaml",
"version": "v2.8.52",
diff --git a/settings/formdata.yaml b/settings/formdata.yaml
new file mode 100644
index 0000000..9423090
--- /dev/null
+++ b/settings/formdata.yaml
@@ -0,0 +1 @@
+''
\ No newline at end of file
diff --git a/system/Controllers/AuthController.php b/system/Controllers/AuthController.php
index 206e83c..83713b9 100644
--- a/system/Controllers/AuthController.php
+++ b/system/Controllers/AuthController.php
@@ -8,6 +8,7 @@ use Slim\Http\Response;
use Typemill\Models\Validation;
use Typemill\Models\User;
use Typemill\Models\WriteYaml;
+use Typemill\Extensions\ParsedownExtension;
class AuthController extends Controller
{
@@ -34,36 +35,10 @@ class AuthController extends Controller
*/
public function show(Request $request, Response $response, $args)
- {
- $data = array();
+ {
+ $settings = $this->c->get('settings');
- /* check previous login attemps */
- $yaml = new WriteYaml();
- $logins = $yaml->getYaml('settings/users', '.logins');
- $userIP = $this->getUserIP();
- $userLogins = isset($logins[$userIP]) ? count($logins[$userIP]) : false;
-
- if($userLogins)
- {
- /* get the latest */
- $lastLogin = intval($logins[$userIP][$userLogins-1]);
-
- /* if last login is longer than 60 seconds ago, clear it. */
- if(time() - $lastLogin > 60)
- {
- unset($logins[$userIP]);
- $yaml->updateYaml('settings/users', '.logins', $logins);
- }
-
- /* Did the user made three login attemps that failed? */
- elseif($userLogins >= 3)
- {
- $timeleft = 60 - (time() - $lastLogin);
- $data['messages'] = array('time' => $timeleft, 'error' => array( 'Too many bad logins. Please wait.'));
- }
- }
-
- return $this->render($response, '/auth/login.twig', $data);
+ return $this->render($response, '/auth/login.twig', ['settings' => $settings]);
}
/**
@@ -79,41 +54,13 @@ class AuthController extends Controller
if( ( null !== $request->getattribute('csrf_result') ) OR ( $request->getattribute('csrf_result') === false ) )
{
$this->c->flash->addMessage('error', 'The form has a timeout, please try again.');
- return $response->withRedirect($this->c->router->pathFor('auth.show'));
+ return $response->withRedirect($this->c->router->pathFor('auth.show'));
}
- /* log user attemps to authenticate */
- $yaml = new WriteYaml();
- $logins = $yaml->getYaml('settings/users', '.logins');
- $userIP = $this->getUserIP();
- $userLogins = isset($logins[$userIP]) ? count($logins[$userIP]) : false;
-
- /* if there have been user logins before. You have to do this again, because user does not always refresh the login page and old login attemps are stored. */
- if($userLogins)
- {
- /* get the latest */
- $lastLogin = intval($logins[$userIP][$userLogins-1]);
-
- /* if last login is longer than 60 seconds ago, clear it and add this attempt */
- if(time() - $lastLogin > 60)
- {
- unset($logins[$userIP]);
- $yaml->updateYaml('settings/users', '.logins', $logins);
- }
-
- /* Did the user made three login attemps that failed? */
- elseif($userLogins >= 2)
- {
- $logins[$userIP][] = time();
- $yaml->updateYaml('settings/users', '.logins', $logins);
-
- return $response->withRedirect($this->c->router->pathFor('auth.show'));
- }
- }
-
- /* authentication */
+ /* authentication */
$params = $request->getParams();
$validation = new Validation();
+ $settings = $this->c->get('settings');
if($validation->signin($params))
{
@@ -131,13 +78,6 @@ class AuthController extends Controller
$user->login($userdata['username']);
- /* clear the user login attemps */
- if($userLogins)
- {
- unset($logins[$userIP]);
- $yaml->updateYaml('settings/users', '.logins', $logins);
- }
-
# if user is allowed to view content-area
if($this->c->acl->hasRole($userdata['userrole']) && $this->c->acl->isAllowed($userdata['userrole'], 'content', 'view'))
{
@@ -149,11 +89,12 @@ class AuthController extends Controller
return $response->withRedirect($this->c->router->pathFor('user.account'));
}
}
-
- /* if authentication failed, add attempt to log file */
- $logins[$userIP][] = time();
- $yaml->updateYaml('settings/users', '.logins', $logins);
+ if(isset($this->settings['securitylog']) && $this->settings['securitylog'])
+ {
+ \Typemill\Models\Helpers::addLogEntry('wrong login');
+ }
+
$this->c->flash->addMessage('error', 'Ups, wrong password or username, please try again.');
return $response->withRedirect($this->c->router->pathFor('auth.show'));
}
@@ -176,25 +117,225 @@ class AuthController extends Controller
return $response->withRedirect($this->c->router->pathFor('auth.show'));
}
- private function getUserIP()
+ public function showRecoverPassword(Request $request, Response $response, $args)
{
- $client = @$_SERVER['HTTP_CLIENT_IP'];
- $forward = @$_SERVER['HTTP_X_FORWARDED_FOR'];
- $remote = $_SERVER['REMOTE_ADDR'];
+ $data = array();
+
+ return $this->render($response, '/auth/recoverpw.twig', $data);
+ }
- if(filter_var($client, FILTER_VALIDATE_IP))
+ public function recoverPassword(Request $request, Response $response, $args)
+ {
+ $params = $request->getParams();
+ $validation = new Validation();
+ $settings = $this->c->get('settings');
+ $uri = $request->getUri()->withUserInfo('');
+ $base_url = $uri->getBaseUrl();
+
+ if(!isset($params['email']) OR filter_var($params['email'], \FILTER_VALIDATE_EMAIL) === false )
{
- $ip = $client;
+ $this->c->flash->addMessage('error', 'Please enter a valid email.');
+ return $response->withRedirect($this->c->router->pathFor('auth.recoverpwshow'));
}
- elseif(filter_var($forward, FILTER_VALIDATE_IP))
+
+ $user = new User();
+ $requiredUser = $user->findUsersByEmail($params['email']);
+
+ if(!$requiredUser)
{
- $ip = $forward;
+ $this->c->flash->addMessage('error', 'The email address is unknown.');
+ return $response->withRedirect($this->c->router->pathFor('auth.recoverpwshow'));
+ }
+
+ $requiredUser = $user->getSecureUser($requiredUser[0]);
+
+ $requiredUser['recoverdate'] = date("Y-m-d H:i:s");
+ $requiredUser['recovertoken'] = bin2hex(random_bytes(32));
+
+ $url = $base_url . '/tm/recoverpwnew?username=' . $requiredUser['username'] . '&recovertoken=' . $requiredUser['recovertoken'];
+ $link = '' . $url . '';
+
+ # define the headers
+ $headers = 'Content-Type: text/html; charset=utf-8' . "\r\n";
+ $headers .= 'Content-Transfer-Encoding: base64' . "\r\n";
+ if(isset($settings['recoverfrom']) && $settings['recoverfrom'] != '')
+ {
+ $headers .= 'From: ' . $settings['recoverfrom'];
+ }
+
+ $subjectline = (isset($settings['recoversubject']) && ($settings['recoversubject'] != '') ) ? $settings['recoversubject'] : 'Recover your password';
+ $subject = '=?UTF-8?B?' . base64_encode($subjectline) . '?=';
+
+ $messagetext = "Dear user,
please use the following link to set a new password:";
+ if(isset($settings['recovermessage']) && ($settings['recovermessage'] != ''))
+ {
+ $parsedown = new ParsedownExtension($base_url);
+ $parsedown->setSafeMode(true);
+
+ $contentArray = $parsedown->text($settings['recovermessage']);
+ $messagetext = $parsedown->markup($contentArray);
+ }
+
+ $message = base64_encode($messagetext . "
" . $link);
+
+ # store user
+ $user->updateUser($requiredUser);
+
+ $send = mail($requiredUser['email'], $subject, $message, $headers);
+
+ if(!$send)
+ {
+ $data = [
+ 'title' => 'We could not send the email',
+ 'message' => 'Dear ' . $requiredUser['username'] . ', we could not send the email with the password instructions to your address. You can try it again but chances are low that it will work next time. Please contact the website owner and ask for help.',
+ ];
}
else
{
- $ip = $remote;
+ # store user
+ $user->updateUser($requiredUser);
+
+ $data = [
+ 'title' => 'Please check your inbox',
+ 'message' => 'Dear ' . $requiredUser['username'] . ', please check the inbox of your email account. We have sent you some short instructions how to recover your password. Do not forget to check your spam-folder if your inbox is empty.',
+ ];
+ }
+
+ return $this->render($response, '/auth/recoverpwsend.twig', $data);
+ }
+
+ public function showRecoverPasswordNew(Request $request, Response $response, $args)
+ {
+ $params = $request->getParams();
+
+ if(!isset($params['username']) OR !isset($params['recovertoken']))
+ {
+ $this->c->flash->addMessage('error', 'Ups, you tried to open the password recovery page but the link was invalid.');
+ return $response->withRedirect($this->c->router->pathFor('auth.show'));
}
- return $ip;
+ $user = new user();
+
+ $requiredUser = $user->getSecureUser($params['username']);
+
+ if(!$requiredUser)
+ {
+ $this->c->flash->addMessage('error', 'Ups, you tried to open the password recovery page but the link was invalid.');
+ return $response->withRedirect($this->c->router->pathFor('auth.show'));
+ }
+
+ if(!isset($requiredUser['recovertoken']) OR $requiredUser['recovertoken'] != $params['recovertoken'] )
+ {
+ $this->c->flash->addMessage('error', 'Ups, you tried to open the password recovery page but the link was invalid.');
+ return $response->withRedirect($this->c->router->pathFor('auth.show'));
+ }
+
+ $recoverdate = isset($requiredUser['recoverdate']) ? $requiredUser['recoverdate'] : false;
+
+ if(!$recoverdate )
+ {
+ $user->unsetFromUser($requiredUser['username'], ['recovertoken']);
+
+ $this->c->flash->addMessage('error', 'The link to recover the password was too old. Please create a new one.');
+ return $response->withRedirect($this->c->router->pathFor('auth.show'));
+ }
+
+ $now = new \DateTime('NOW');
+ $recoverdate = new \DateTime($recoverdate);
+
+ if(!$recoverdate)
+ {
+ $user->unsetFromUser($requiredUser['username'], ['recovertoken', 'recoverdate']);
+
+ $this->c->flash->addMessage('error', 'The link to recover the password was too old. Please create a new one.');
+ return $response->withRedirect($this->c->router->pathFor('auth.show'));
+ }
+
+ $validDate = $recoverdate->add(new \DateInterval('P1D'));
+
+ if($validDate <= $now)
+ {
+ $user->unsetFromUser($requiredUser['username'], ['recovertoken', 'recoverdate']);
+
+ $this->c->flash->addMessage('error', 'The link to recover the password was too old. Please create a new one.');
+ return $response->withRedirect($this->c->router->pathFor('auth.show'));
+ }
+
+ return $this->render($response, '/auth/recoverpwnew.twig', ['recovertoken' => $params['recovertoken'],'username' => $requiredUser['username']]);
+ }
+
+ public function createRecoverPasswordNew(Request $request, Response $response, $args)
+ {
+ $params = $request->getParams();
+
+ if(!isset($params['username']) OR !isset($params['recovertoken']))
+ {
+ $this->c->flash->addMessage('error', 'Ups, you tried to set a new password but username or token was invalid.');
+ return $response->withRedirect($this->c->router->pathFor('auth.show'));
+ }
+
+ $validation = new Validation();
+
+ if(!$validation->recoverPassword($params))
+ {
+ $this->c->flash->addMessage('error', 'Please check your input.');
+ return $response->withRedirect($this->c->router->pathFor('auth.recoverpwshownew',[], ['username' => $params['username'], 'recovertoken' => $params['recovertoken']]));
+ }
+
+ $user = new user();
+
+ $requiredUser = $user->getSecureUser($params['username']);
+
+ if(!$requiredUser)
+ {
+ $this->c->flash->addMessage('error', 'Ups, you tried to create a new password but the username was invalid.');
+ return $response->withRedirect($this->c->router->pathFor('auth.show'));
+ }
+
+ if(!isset($requiredUser['recovertoken']) OR $requiredUser['recovertoken'] != $params['recovertoken'] )
+ {
+ $this->c->flash->addMessage('error', 'Ups, you tried to create a new password but the token was invalid.');
+ return $response->withRedirect($this->c->router->pathFor('auth.show'));
+ }
+
+ $recoverdate = isset($requiredUser['recoverdate']) ? $requiredUser['recoverdate'] : false;
+
+ if(!$recoverdate )
+ {
+ $user->unsetFromUser($requiredUser['username'], ['recovertoken']);
+
+ $this->c->flash->addMessage('error', 'The date for the password reset was invalid. Please create a new one.');
+ return $response->withRedirect($this->c->router->pathFor('auth.show'));
+ }
+
+ $now = new \DateTime('NOW');
+ $recoverdate = new \DateTime($recoverdate);
+
+ if(!$recoverdate)
+ {
+ $user->unsetFromUser($requiredUser['username'], ['recovertoken', 'recoverdate']);
+
+ $this->c->flash->addMessage('error', 'The date for the password reset was too old. Please create a new one.');
+ return $response->withRedirect($this->c->router->pathFor('auth.show'));
+ }
+
+ $validDate = $recoverdate->add(new \DateInterval('P1D'));
+
+ if($validDate <= $now)
+ {
+ $user->unsetFromUser($requiredUser['username'], ['recovertoken', 'recoverdate']);
+
+ $this->c->flash->addMessage('error', 'The link to recover the password was too old. Please create a new one.');
+ return $response->withRedirect($this->c->router->pathFor('auth.show'));
+ }
+
+ $requiredUser['password'] = $params['password'];
+ $user->updateUser($requiredUser);
+ $user->unsetFromUser($requiredUser['username'], ['recovertoken', 'recoverdate']);
+
+ unset($_SESSION['old']);
+
+ $this->c->flash->addMessage('info', 'A new password has been created. Please login.');
+ return $response->withRedirect($this->c->router->pathFor('auth.show'));
}
}
\ No newline at end of file
diff --git a/system/Controllers/FormController.php b/system/Controllers/FormController.php
index 4e77827..a9c4b86 100644
--- a/system/Controllers/FormController.php
+++ b/system/Controllers/FormController.php
@@ -12,7 +12,7 @@ class FormController extends Controller
*************************************/
public function savePublicForm($request, $response, $args)
- {
+ {
if($request->isPost())
{
$params = $request->getParams();
@@ -26,46 +26,6 @@ class FormController extends Controller
$this->c->flash->addMessage('error', 'The form has a timeout. Please try again.');
return $response->withRedirect($referer[0]);
}
-
- # simple bot check with honeypot
- if(isset($params[$pluginName]['personal-mail']))
- {
- if($params[$pluginName]['personal-mail'] != '')
- {
- $this->c->flash->addMessage('publicform', 'bot');
- return $response->withRedirect($referer[0]);
- }
- unset($params[$pluginName]['personal-mail']);
- }
-
- #recaptcha check
- if(isset($params['g-recaptcha-response']))
- {
- $recaptchaApi = 'https://www.google.com/recaptcha/api/siteverify';
- $settings = $this->c->get('settings');
- $secret = isset($settings['plugins'][$pluginName]['recaptcha_secretkey']) ? $settings['plugins'][$pluginName]['recaptcha_secretkey'] : false;
- $recaptchaRequest = ['secret' => $secret, 'response' => $params['g-recaptcha-response']];
-
- # use key 'http' even if you send the request to https://...
- $options = array(
- 'http' => array(
- 'header' => "Content-type: application/x-www-form-urlencoded\r\n",
- 'method' => 'POST',
- 'content' => http_build_query($recaptchaRequest),
- 'timeout' => 5
- )
- );
-
- $context = stream_context_create($options);
- $result = file_get_contents($recaptchaApi, false, $context);
- $result = json_decode($result);
-
- if ($result === FALSE || $result->success === FALSE)
- {
- $this->c->flash->addMessage('publicform', 'bot');
- return $response->withRedirect($referer[0]);
- }
- }
if(isset($params[$pluginName]))
{
diff --git a/system/Controllers/SettingsController.php b/system/Controllers/SettingsController.php
index 5d32191..f73b4b6 100644
--- a/system/Controllers/SettingsController.php
+++ b/system/Controllers/SettingsController.php
@@ -113,6 +113,11 @@ class SettingsController extends Controller
'headersoff' => isset($newSettings['headersoff']) ? true : null,
'urlschemes' => $newSettings['urlschemes'],
'svg' => isset($newSettings['svg']) ? true : null,
+ 'recoverpw' => isset($newSettings['recoverpw']) ? true : null,
+ 'recoverfrom' => $newSettings['recoverfrom'],
+ 'recoversubject' => $newSettings['recoversubject'],
+ 'recovermessage' => $newSettings['recovermessage'],
+ 'securitylog' => isset($newSettings['securitylog']) ? true : null,
);
# https://www.slimframework.com/docs/v3/cookbook/uploading-files.html;
@@ -348,15 +353,7 @@ class SettingsController extends Controller
/* if the plugin defines forms and fields, so that the user can edit the plugin settings in the frontend */
if(isset($pluginOriginalSettings['forms']['fields']))
- {
- # if the plugin defines frontend fields
- if(isset($pluginOriginalSettings['public']))
- {
- $pluginOriginalSettings['forms']['fields']['recaptcha'] = ['type' => 'checkbox', 'label' => 'Google Recaptcha', 'checkboxlabel' => 'Activate Recaptcha' ];
- $pluginOriginalSettings['forms']['fields']['recaptcha_webkey'] = ['type' => 'text', 'label' => 'Recaptcha Website Key', 'help' => 'Add the recaptcha website key here. You can get the key from the recaptcha website.', 'description' => 'The website key is mandatory if you activate the recaptcha field'];
- $pluginOriginalSettings['forms']['fields']['recaptcha_secretkey'] = ['type' => 'text', 'label' => 'Recaptcha Secret Key', 'help' => 'Add the recaptcha secret key here. You can get the key from the recaptcha website.', 'description' => 'The secret key is mandatory if you activate the recaptcha field'];
- }
-
+ {
/* get all the fields and prefill them with the dafault-data, the user-data or old input data */
$fields = $fieldsModel->getFields($userSettings, 'plugins', $pluginName, $pluginOriginalSettings);
@@ -1132,14 +1129,6 @@ class SettingsController extends Controller
}
}
- # if the plugin defines frontend fields
- if(isset($originalSettings['public']))
- {
- $originalFields['recaptcha'] = ['type' => 'checkbox', 'label' => 'Google Recaptcha', 'checkboxlabel' => 'Activate Recaptcha' ];
- $originalFields['recaptcha_webkey'] = ['type' => 'text', 'label' => 'Recaptcha Website Key', 'help' => 'Add the recaptcha website key here. You can get the key from the recaptcha website.', 'description' => 'The website key is mandatory if you activate the recaptcha field'];
- $originalFields['recaptcha_secretkey'] = ['type' => 'text', 'label' => 'Recaptcha Secret Key', 'help' => 'Add the recaptcha secret key here. You can get the key from the recaptcha website.', 'description' => 'The secret key is mandatory if you activate the recaptcha field'];
- }
-
# if plugin is not active, then skip required
$skiprequired = false;
if($objectType == 'plugins' && !isset($userInput['active']))
diff --git a/system/Extensions/TwigCaptchaExtension.php b/system/Extensions/TwigCaptchaExtension.php
new file mode 100644
index 0000000..0dc18cc
--- /dev/null
+++ b/system/Extensions/TwigCaptchaExtension.php
@@ -0,0 +1,44 @@
+build();
+
+ $error = '';
+ if(isset($_SESSION['captcha']) && $_SESSION['captcha'] === 'error')
+ {
+ $error = 'The captcha was wrong.';
+ }
+
+ $_SESSION['phrase'] = $builder->getPhrase();
+
+ $_SESSION['captcha'] = true;
+
+ $template = '
Breakpoint | Time | Duration |
---|---|---|
' . $breakpoint . ' | '; - $table .= '' . $time . ' | '; - $table .= '' . $duration . ' | '; - $table .= '
Breakpoint | Time | Duration |
---|---|---|
' . $breakpoint . ' | '; + $table .= '' . $time . ' | '; + $table .= '' . $duration . ' | '; + $table .= '
{{ __('back to startpage') }}{{ __('forgot password') }}
+ + {% else %} + + + + {% endif %} + {% endblock %} \ No newline at end of file diff --git a/system/author/auth/recoverpw.twig b/system/author/auth/recoverpw.twig new file mode 100644 index 0000000..e8b2685 --- /dev/null +++ b/system/author/auth/recoverpw.twig @@ -0,0 +1,39 @@ +{% extends 'layouts/layoutAuth.twig' %} +{% block title %}Login{% endblock %} + +{% block content %} + +{{ message }}
+ +