1
0
mirror of https://github.com/typemill/typemill.git synced 2025-08-04 21:27:41 +02:00

Captcha integration

This commit is contained in:
trendschau
2023-09-15 22:08:05 +02:00
parent 089af76dde
commit ea2821a2a4
11 changed files with 210 additions and 64 deletions

View File

@@ -1,8 +1,18 @@
meta:
navtitle: 'write content'
owner: Sebastian
created: '2023-06-12'
time: 22-09-48
modified: '2023-05-11'
title: 'Write Content'
description: 'Typemill provides easy and intuitive authoring tools and we work hard to create a good author experience. With the interactive navigation you can create pages'
heroimage: null
heroimagealt: null
owner: Sebastian
author: null
allowedrole: null
alloweduser: null
manualdate: null
modified: '2023-05-11'
created: '2023-06-12'
time: 22-09-48
reference: null
referencetype: null
hide: false
noindex: false

View File

@@ -1 +1 @@
["# ToDos Version 2","[TOC]","## System settings","* DONE: Migrate from backend to frontend with vue and api\n* DONE: Redesign\n* DONE: License feature\n* DONE: Enhance with plugins","## Visual Editor","* DONE: Refactor and redesign\n* DONE: Fix toc component in new block\n* DONE: Fix hr component in new block\n* DONE: finish shortcode component\n* DONE: Fix inline formats\n* DONE: fix lenght of page\n* DONE: Fix design of new block at the end (background color)\n* DONE: Move Block\n* DONE: Fix headline design\n* DONE: Fix save on two enter\n* DONE: fix quote design\n* DONE: Fix toc preview\n* DONE: disable enable \n* DONE: Add load sign (from navigation)\n* DONE: File is not published from tmp to media\/files if you save the block.","## Raw Editor","* DONE: Refactor and redesign\n* DONE: Integrate highlighting","## Navigation","* DONE: Refactor and redesign\n* DONE: fix status in navigation\n* DONE: refresh navigation after changes","## Publish Controller","* DONE: Refactor and redesign\n* DONE: Create \n* DONE: publish\n* DONE: unpublish\n* DONE: discard\n* DONE: delete\n* DONE: save draft\n* DONE: switch to raw","## Meta Tabs","* DONE: Refactor and redesign\n* DONE: Enhance with plugins","## Medialib","* DONE: Refactor and redesign","## Posts","* DONE: Refactor and redesign","## Plugins","* Asset Class in progress","## Frontend","* DONE: Refactor\n* DONE: Test restrictions","## Other big tasks","* DONE: System setup\n* DONE: Recover Password","## Medium tasks","* DONE: Merge processAssets modell\n* DONE: Table of content duplicated for published pages\n* DONE: Session handling: csrf fail and session start error if restrictions are active\n* DONE: Image and files for meta","## Open tasks","* DONE: Sitemap and ping\n* DONE: Version check\n* DONE: Proxy support\n* DONE: SVG checker: https:\/\/github.com\/TribalSystems\/SVG-Sanitizer\n* DONE: Backend form builder\n* DONE: Image generation on the fly\n* DONE: Delete folder in base level\n* DONE: Make folder delete easier with glob or scandir\n* DONE: fix error messages (check models)\n* DONE: error status codes (check middleware)\n* DONE: Warn if open another block\n* DONE: Customfields not styled yet\n* DOING: Fix error api systemnavi + validate\n* FIXED: System stores html or sends wrong error messsages\n* FIXED: Wrong frontend navigation if unpublished pages\n* DOING: Responsive design\n* Captcha integration\n* Reference feature\n* Typemill Utilities\n* Handle formdata centrally ???\n* Markdown secure rendering\n* finish youtube component\n* Solution for logo and favicon\n* BUG: Raw editor jumps if you edit long text at the end\n* BUG: Error fields in account form not styled correctly\n* Icon for hidden pages","## later","* Clear cache\n* Show security Log\n* User search only for +10 users\n* For api translations should be done completely in backoffice\n* Change translation files so they are loaded in settings instead of adding them manually to settings-defaults.yaml","## Cleanups:","* DONE: Events\n* DONE: Error messages\n* DONE: Translations","## Info: Select userroles","* Userroles for file restriction: in vue-blox-components loaded via api\n* Userroles for userfields: in php model user getUserFields()\n* Userroles for meta: in php controller apiAuthorMeta getMeta()\n* Plugins and themes: in php model extension getThemeDefinitions()","## Info: License Check","* On activation in apiControllerExtension. It checks the license in yaml.\n* In plugin php code with setPremiumLicense\n* In static plugins, it checks manual premium list and method setPremiumLicense and more ","## Plugins","* MAKER: Rebuild search\n* MAKER: Rebuild contactform with shortcode","## Status codes","| Status code | Description | \n|---|---|\n| 200 ok | cell | \n| 400 bad request | The request was unacceptable due to missing or invalid parameter. | \n| 401 unauthorized | The request requires an authorization. | \n| (402 request failed) | The parameters where there but the request failed for other reasons. | \n| 403 forbidden | The user is authenticated but he has not enough rights. | \n| 404 not found | new | \n| 500 internal server error | new |"]
["# ToDos Version 2","[TOC]","## System settings","* DONE: Migrate from backend to frontend with vue and api\n* DONE: Redesign\n* DONE: License feature\n* DONE: Enhance with plugins","## Visual Editor","* DONE: Refactor and redesign\n* DONE: Fix toc component in new block\n* DONE: Fix hr component in new block\n* DONE: finish shortcode component\n* DONE: Fix inline formats\n* DONE: fix lenght of page\n* DONE: Fix design of new block at the end (background color)\n* DONE: Move Block\n* DONE: Fix headline design\n* DONE: Fix save on two enter\n* DONE: fix quote design\n* DONE: Fix toc preview\n* DONE: disable enable \n* DONE: Add load sign (from navigation)\n* DONE: File is not published from tmp to media\/files if you save the block.","## Raw Editor","* DONE: Refactor and redesign\n* DONE: Integrate highlighting","## Navigation","* DONE: Refactor and redesign\n* DONE: fix status in navigation\n* DONE: refresh navigation after changes","## Publish Controller","* DONE: Refactor and redesign\n* DONE: Create \n* DONE: publish\n* DONE: unpublish\n* DONE: discard\n* DONE: delete\n* DONE: save draft\n* DONE: switch to raw","## Meta Tabs","* DONE: Refactor and redesign\n* DONE: Enhance with plugins","## Medialib","* DONE: Refactor and redesign","## Posts","* DONE: Refactor and redesign","## Plugins","* Asset Class in progress","## Frontend","* DONE: Refactor\n* DONE: Test restrictions","## Other big tasks","* DONE: System setup\n* DONE: Recover Password","## Medium tasks","* DONE: Merge processAssets modell\n* DONE: Table of content duplicated for published pages\n* DONE: Session handling: csrf fail and session start error if restrictions are active\n* DONE: Image and files for meta","## Open tasks","* DONE: Sitemap and ping\n* DONE: Version check\n* DONE: Proxy support\n* DONE: SVG checker: https:\/\/github.com\/TribalSystems\/SVG-Sanitizer\n* DONE: Backend form builder\n* DONE: Image generation on the fly\n* DONE: Delete folder in base level\n* DONE: Make folder delete easier with glob or scandir\n* DONE: fix error messages (check models)\n* DONE: error status codes (check middleware)\n* DONE: Warn if open another block\n* DONE: Customfields not styled yet\n* DOING: Fix error api systemnavi + validate\n* FIXED: System stores html or sends wrong error messsages\n* FIXED: Wrong frontend navigation if unpublished pages\n* DONE: Icon for hidden pages\n* DOING: Responsive design\n*DONE: Captcha integration\n* Reference feature\n* Typemill Utilities\n* Markdown secure rendering\n* finish youtube component\n* Solution for logo and favicon\n* Handle formdata centrally ???\n* BUG: Raw editor jumps if you edit long text at the end\n* BUG: Error fields in account form not styled correctly","## later","* Clear cache\n* Show security Log\n* User search only for +10 users\n* For api translations should be done completely in backoffice\n* Change translation files so they are loaded in settings instead of adding them manually to settings-defaults.yaml","## Cleanups:","* DONE: Events\n* DONE: Error messages\n* DONE: Translations","## Info: Select userroles","* Userroles for file restriction: in vue-blox-components loaded via api\n* Userroles for userfields: in php model user getUserFields()\n* Userroles for meta: in php controller apiAuthorMeta getMeta()\n* Plugins and themes: in php model extension getThemeDefinitions()","## Info: License Check","* On activation in apiControllerExtension. It checks the license in yaml.\n* In plugin php code with setPremiumLicense\n* In static plugins, it checks manual premium list and method setPremiumLicense and more ","## Plugins","* MAKER: Rebuild search\n* MAKER: Rebuild contactform with shortcode","## Status codes","| Status code | Description | \n|---|---|\n| 200 ok | cell | \n| 400 bad request | The request was unacceptable due to missing or invalid parameter. | \n| 401 unauthorized | The request requires an authorization. | \n| (402 request failed) | The parameters where there but the request failed for other reasons. | \n| 403 forbidden | The user is authenticated but he has not enough rights. | \n| 404 not found | new | \n| 500 internal server error | new |"]

View File

@@ -2,3 +2,11 @@
127.0.0.1;2023-07-26 15:24:27;wrong input for password recovery
127.0.0.1;2023-07-26 15:25:30;wrong input for password recovery
127.0.0.1;2023-07-29 22:34:30;wrong login
127.0.0.1;2023-09-13 21:18:22;honeypot http://localhost/typemill/tm/login
127.0.0.1;2023-09-13 21:26:34;honeypot http://localhost/typemill/tm/login
127.0.0.1;2023-09-13 21:29:10;honeypot http://localhost/typemill/tm/login
127.0.0.1;2023-09-13 21:42:59;wrong captcha http://localhost/typemill/tm/login
127.0.0.1;2023-09-13 22:18:42;wrong captcha http://localhost/typemill/tm/login
127.0.0.1;2023-09-13 22:20:17;wrong captcha http://localhost/typemill/tm/login
127.0.0.1;2023-09-15 06:06:46;wrong login
127.0.0.1;2023-09-15 22:03:38;wrong login

View File

@@ -15,7 +15,7 @@ class ControllerWebAuth extends Controller
{
return $this->c->get('view')->render($response, 'auth/login.twig', [
'recover' => $this->settings['recoverpw'] ?? false,
#'captcha' => $this->checkIfAddCaptcha(),
'captcha' => $this->settings['authcaptcha'] ?? false,
]);
}

View File

@@ -17,29 +17,33 @@ class TwigCaptchaExtension extends AbstractExtension
public function captchaImage($initialize = false)
{
if(isset($_SESSION['captcha']) OR $initialize)
{
$builder = new CaptchaBuilder;
$builder->build();
$error = '';
if(isset($_SESSION['captcha']) && $_SESSION['captcha'] === 'error')
{
$error = '<span class="error">The captcha was wrong.</span>';
$template = '<div class="my-2 error">' .
'<label for="captcha">Captcha</label>' .
'<input type="text" name="captcha" class="form-control block w-full px-3 py-1.5 text-base font-normal text-gray-700 bg-white bg-clip-padding border border-solid border-red-500 bg-red-100 transition ease-in-out m-0 focus:text-gray-700 focus:bg-white focus:border-blue-600 focus:outline-none">' .
'<span class="text-xs">The captcha was wrong.</span>' .
'<img class="captcha my-2" src="' . $builder->inline() . '" />' .
'</div>';
}
else
{
$template = '<div class="my-2">' .
'<label for="captcha">Captcha</label>' .
'<input type="text" name="captcha" class="form-control block w-full px-3 py-1.5 text-base font-normal text-gray-700 bg-white bg-clip-padding border border-solid border-gray-300 transition ease-in-out m-0 focus:text-gray-700 focus:bg-white focus:border-blue-600 focus:outline-none">' .
'<img class="captcha my-2" src="' . $builder->inline() . '" />' .
'</div>';
}
$_SESSION['phrase'] = $builder->getPhrase();
$_SESSION['captcha'] = true;
$template = '<div class="formElement">' .
'<label for="captcha">Captcha</label>' .
'<input type="text" name="captcha">' .
$error .
'<img class="captcha" src="' . $builder->inline() . '" />' .
'</div>';
return $template;
}
}

View File

@@ -28,7 +28,7 @@ class ApiAuthorization implements MiddlewareInterface
'message' => $message
]));
return $response->withStatus(403);
return $response->withStatus(403);
}
$response = $handler->handle($request);

View File

@@ -0,0 +1,138 @@
<?php
namespace Typemill\Middleware;
#use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Server\RequestHandlerInterface as RequestHandler;
use Psr\Http\Server\MiddlewareInterface;
use Slim\Psr7\Response;
use Slim\Routing\RouteParser;
use Gregwar\Captcha\CaptchaBuilder;
class securityMiddleware implements MiddlewareInterface
{
protected $router;
protected $settings;
# protected $flash;
public function __construct(RouteParser $router, $settings)
{
$this->router = $router;
$this->settings = $settings;
# $this->flash = $flash;
}
public function process(Request $request, RequestHandler $handler) :Response
{
if($request->getMethod() == 'POST')
{
$params = $request->getParsedBody();
$referer = $request->getHeader('HTTP_REFERER');
# simple bot check with honeypot
if(
(isset($params['personal-honey-mail']))
&& (null !== $params['personal-honey-mail'])
&& ($params['personal-honey-mail'] != '')
)
{
if(isset($this->settings['securitylog']) && $this->settings['securitylog'])
{
\Typemill\Static\Helpers::addLogEntry('honeypot ' . $referer[0]);
}
$response = new Response();
return $response->withHeader('Location', $referer[0])->withStatus(302);
# return $response->withHeader('Location', $this->router->urlFor('auth.login'))->withStatus(302);
}
# check captcha
if(isset($_SESSION['captcha']))
{
# if captcha field was filled correctly
if(
(isset($params['captcha']))
&& (null !== $params['captcha'])
&& \Gregwar\Captcha\PhraseBuilder::comparePhrases($_SESSION['phrase'], $params['captcha'] )
)
{
# delete captcha because it is solved and should not show up again
unset($_SESSION['captcha']);
# delete phrase because can't use twice
unset($_SESSION['phrase']);
}
else
{
# delete phrase because can't use twice, but keep captcha so it shows up again
unset($_SESSION['phrase']);
# set session to error
$_SESSION['captcha'] = 'error';
if(
isset($this->settings['securitylog'])
&& $this->settings['securitylog']
)
{
\Typemill\Static\Helpers::addLogEntry('wrong captcha ' . $referer[0]);
}
# and add message that captcha is empty
# $this->flash->addMessage('error', 'Captcha is wrong.');
$response = new Response();
return $response->withHeader('Location', $referer[0])->withStatus(302);
}
}
/*
#check google recaptcha
if( null !== $request->getParam('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' => $request->getParam('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)
{
if(isset($this->settings['securitylog']) && $this->settings['securitylog'])
{
\Typemill\Models\Helpers::addLogEntry('wrong google recaptcha ' . $referer[0]);
}
# and add message that captcha is empty
$this->flash->addMessage('error', 'Captcha is wrong.');
return $response->withRedirect($referer[0]);
}
}
*/
}
$response = $handler->handle($request);
return $response;
}
}

View File

@@ -46,6 +46,16 @@
<input type="text" name="personal-honey-mail">
</div>
{% if captcha == 'standard' %}
{{ captcha(true) }}
{% elseif captcha == 'aftererror' %}
{{ captcha(old) }}
{% endif %}
<input
type="submit"
value="{{ translate('Login') }}"

View File

@@ -152,7 +152,7 @@ navigation.component('navilevel',{
:expanded="expanded"
>
<template #item="{ element }">
<li :class="element.elementType" :id="element.keyPath" :data-url="element.urlRelWoF" :data-active="element.active">
<li :class="element.elementType" :id="element.keyPath" :data-url="element.urlRelWoF" :data-active="element.active" :data-hide="element.hide">
<div class="flex w-full my-px border-b border-stone-200 relative" :class="element.elementType == 'folder' ? 'font-bold' : ''">
<div class="border-l-4" :class="getStatusClass(element.status)"></div>
<a :href="getUrl(element.urlRelWoF)" class="flex-grow border-l-2 border-stone-50 p-1 hover:bg-teal-500 hover:text-stone-50" :class="getNaviClass(element.active, element.activeParent, element.keyPathArray)">
@@ -164,6 +164,11 @@ navigation.component('navilevel',{
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
</div>
<div v-if="element.hide" class="p-1 absolute right-0">
<svg class="icon icon-eye-blocked">
<use xlink:href="#icon-eye-blocked"></use>
</svg>
</div>
<div v-if="element.elementType == 'folder' && element.contains == 'pages'" class=" p-1 bg-transparent absolute right-0" @click="callToggle(element.name)">
<svg v-if="isExpanded(element.name)" class="icon icon-cheveron-up">
<use xlink:href="#icon-cheveron-up"></use>

View File

@@ -154,10 +154,21 @@ fieldsetaccess:
type: checkbox
label: Wrap restriction notice
checkboxlabel: Wrap the restriction notice above into a notice-4 element (which can be designed as special box)
fieldsetrecovery:
fieldsetsecurity:
type: fieldset
legend: PW recovery
legend: Security
fields:
securitylog:
type: checkbox
label: Security log
checkboxlabel: Track spam and suspicious actions in a logfile
authcaptcha:
type: radio
label: Use captcha in authentication forms
options:
disabled: Disable
standard: Always show
aftererror: Show after first wrong input
recoverpw:
type: checkbox
label: Recover password
@@ -184,10 +195,6 @@ fieldsetdeveloper:
type: checkbox
label: Error reporting
checkboxlabel: Display application errors
securitylog:
type: checkbox
label: Security log
checkboxlabel: Track spam and suspicious actions in a logfile
twigcache:
type: checkbox
label: Twig cache

View File

@@ -27,6 +27,7 @@ use Typemill\Middleware\ValidationErrorsMiddleware;
use Typemill\Middleware\JsonBodyParser;
use Typemill\Middleware\FlashMessages;
use Typemill\Middleware\AssetMiddleware;
use Typemill\Middleware\SecurityMiddleware;
use Typemill\Extensions\TwigCsrfExtension;
use Typemill\Extensions\TwigUrlExtension;
use Typemill\Extensions\TwigUserExtension;
@@ -53,7 +54,6 @@ error_reporting(E_ALL);
$settingsModel = new Settings();
$settings = $settingsModel->loadSettings();
# $settings = Settings::loadSettings();
/****************************
* HANDLE DISPLAY ERRORS *
@@ -244,39 +244,11 @@ $container->set('dispatcher', function() use ($dispatcher){ return $dispatcher;
$assets = new \Typemill\Assets($urlinfo['basepath']);
$container->set('assets', function() use ($assets){ return $assets; });
# Register Middleware On Container
$csrf = false;
/*
if(isset($_SESSION))
{
# add flash messsages
$container->set('flash', function(){ return new Messages(); });
# Register Middleware On Container
$csrf = new Guard($responseFactory);
$csrf->setPersistentTokenMode(true);
$request = $responseFactory->;
$csrf->setFailureHandler(
function (ServerRequestInterface $request, RequestHandlerInterface $handler)
{
$request = $request->withAttribute("csrf_status", false);
return $handler->handle($request);
}
);
$container->set('csrf', function () use ($csrf){ return $csrf; });
# Add Validation Errors Middleware
# $app->add(new ValidationErrors($container->get('view')));
}
*/
/****************************
* TWIG TO CONTAINER *
****************************/
$container->set('view', function() use ($settings, $csrf, $urlinfo, $translations) {
$container->set('view', function() use ($settings, $urlinfo, $translations) {
$twig = Twig::create(
[
@@ -306,20 +278,10 @@ $container->set('view', function() use ($settings, $csrf, $urlinfo, $translation
$twig->addExtension(new TwigMetaExtension());
$twig->addExtension(new TwigCaptchaExtension());
# start csrf only if session is active
/*
if($csrf)
{
$twig->addExtension(new TwigCsrfExtension($csrf));
}
*/
return $twig;
});
/****************************
* MIDDLEWARE *
****************************/
@@ -328,6 +290,8 @@ $app->add(new AssetMiddleware($assets, $container->get('view')));
$app->add(new ValidationErrorsMiddleware($container->get('view')));
$app->add(new SecurityMiddleware($routeParser, $container->get('settings')));
$app->add(new OldInputMiddleware($container->get('view')));
$app->add(new FlashMessages($container));