Compare commits

...
Author SHA1 Message Date
bence d30ec3a3a0 Merge pull request 'feature/MAPG-242-add-captcha-for-signup-and-password-reset' (!54) from feature/MAPG-242-add-captcha-for-signup-and-password-reset into develop
mapguesser/pipeline/head This commit looks good
Reviewed-on: https://gitea.e5tv.hu/esoko/mapguesser/pulls/54
2022-05-26 18:47:46 +02:00
bence cc19d454fa MAPG-242 add captcha validation for password reset
mapguesser/pipeline/pr-develop This commit looks good
2022-05-26 18:45:16 +02:00
bence 241d2f2b30 MAPG-242 add captcha validation for signup 2022-05-26 18:41:26 +02:00
bence d0751017db MAPG-242 add possibility to captcha validation 2022-05-26 18:39:33 +02:00
bence 6014e4517a Merge pull request 'MAPG-241 use class 'text' for text inputs' (!53) from bugfix/MAPG-241-fix-text-input-styles into develop
mapguesser/pipeline/head This commit looks good
Reviewed-on: https://gitea.e5tv.hu/esoko/mapguesser/pulls/53
2022-05-26 15:58:22 +02:00
bence 81a8153866 MAPG-241 use class 'text' for text inputs
mapguesser/pipeline/pr-develop This commit looks good
2022-05-26 15:55:45 +02:00
bence e435ef6be0 Merge pull request 'feature/MAPG-231-make-it-possible-to-create-unlisted-maps' (!52) from feature/MAPG-231-make-it-possible-to-create-unlisted-maps into develop
mapguesser/pipeline/head This commit looks good
Reviewed-on: https://gitea.e5tv.hu/esoko/mapguesser/pulls/52
2022-05-26 14:34:48 +02:00
bence 90528e61e5 MAPG-231 cleanup input styles
mapguesser/pipeline/pr-develop This commit looks good
2022-05-26 14:23:29 +02:00
bence d02aa4abe0 MAPG-231 introduce unlisted maps 2022-05-26 14:23:08 +02:00
19 changed files with 148 additions and 79 deletions

No files matched your search

+2
View File
@@ -21,3 +21,5 @@ MULTI_INTERNAL_PORT=5000
MULTI_WS_URL=mapguesser-dev.ch:8090
MULTI_WS_PORT=8090
ENABLE_GAME_FOR_GUESTS=0
RECAPTCHA_SITEKEY=your_recaptcha_sitekey
RECAPTCHA_SECRET=your_recaptcha_secret
@@ -0,0 +1,2 @@
ALTER TABLE `maps`
ADD `unlisted` TINYINT(1) NOT NULL DEFAULT 0;
+14 -10
View File
@@ -241,7 +241,7 @@ button.green:enabled:hover, button.green:enabled:focus, a.button.green:hover, a.
background-color: #1b7d31;
}
input, select, textarea {
input.text, select, textarea {
background-color: #f9fafb;
border: solid #c8d2e1 1px;
border-radius: 2px;
@@ -250,22 +250,26 @@ input, select, textarea {
font-weight: 300;
}
input, select {
input.text, select {
height: 30px;
line-height: 30px;
padding: 0 5px;
}
input[type=checkbox], input[type=radio] {
margin-right: 0.5em;
}
textarea {
padding: 5px;
resize: none;
}
input.big, select.big, textarea.big, div.inputWithButton>input {
input.text.big, select.big, textarea.big, div.inputWithButton>input.text {
font-size: 18px;
}
input.big, select.big, div.inputWithButton>input {
input.text.big, select.big, div.inputWithButton>input.text {
height: 35px;
line-height: 35px;
padding: 0 6px;
@@ -280,19 +284,19 @@ input.fullWidth, select.fullWidth, textarea.fullWidth {
width: 100%;
}
input:disabled, select:disabled, textarea:disabled {
input.text:disabled, select:disabled, textarea:disabled {
background-color: #dfdfdf;
border: solid #dfdfdf 1px;
color: #000000;
}
input:focus, select:focus, textarea:focus {
input.text:focus, select:focus, textarea:focus {
background-color: #ffffff;
border: solid #29457f 2px;
outline: none;
}
input:focus, select:focus {
input.text:focus, select:focus {
padding: 0 4px;
}
@@ -300,16 +304,16 @@ textarea:focus {
padding: 4px;
}
input.big:focus, select.big:focus {
input.text.big:focus, select.big:focus {
padding: 0 5px;
}
div.inputWithButton>input {
div.inputWithButton>input.text {
width: 100%;
padding: 0 83px 0 6px;
}
div.inputWithButton>input:focus {
div.inputWithButton>input.text:focus {
padding: 0 82px 0 5px;
}
+5 -12
View File
@@ -13,6 +13,10 @@ div.mapItem.new {
align-items: center;
}
div.mapItem.unlisted {
opacity: 0.6;
}
div.mapItem>div.title {
background-color: #28a745;
color: white;
@@ -75,19 +79,8 @@ div.mapItem>div.buttonContainer {
grid-auto-flow: column;
}
#restrictions input {
height: auto;
margin: 0.5em;
}
#restrictions input[type=range] {
height: 1.5em;
margin-left: 2em;
width: 70%;
}
#timeLimitType {
margin-left: 2em;
margin-left: 1.5em;
}
@media screen and (min-width: 1504px) {
+4
View File
@@ -18,6 +18,7 @@
MapEditor.metadata.name = form.elements.name.value;
MapEditor.metadata.description = form.elements.description.value;
MapEditor.metadata.unlisted = form.elements.unlisted.checked;
document.getElementById('mapName').innerHTML = form.elements.name.value ? form.elements.name.value : '[unnamed map]';
@@ -254,6 +255,9 @@
if (MapEditor.metadata.description !== null) {
data.append('description', MapEditor.metadata.description);
}
if (MapEditor.metadata.unlisted !== null) {
data.append('unlisted', MapEditor.metadata.unlisted);
}
for (var placeId in MapEditor.added) {
if (!MapEditor.added.hasOwnProperty(placeId)) {
+25
View File
@@ -19,6 +19,7 @@ use MapGuesser\Repository\UserRepository;
use MapGuesser\Response\HtmlContent;
use MapGuesser\Response\JsonContent;
use MapGuesser\Response\Redirect;
use MapGuesser\Util\CaptchaValidator;
use MapGuesser\Util\JwtParser;
class LoginController
@@ -285,6 +286,18 @@ class LoginController
return new JsonContent($data);
}
if (!empty($_ENV['RECAPTCHA_SITEKEY'])) {
if (!$this->request->post('g-recaptcha-response')) {
return new JsonContent(['error' => ['errorText' => 'Please check "I\'m not a robot" in the reCAPTCHA box!']]);
}
$captchaValidator = new CaptchaValidator();
$captchaResponse = $captchaValidator->validate($this->request->post('g-recaptcha-response'));
if (!$captchaResponse['success']) {
return new JsonContent(['error' => ['errorText' => 'reCAPTCHA challenge failed. Please try again!']]);
}
}
if (filter_var($this->request->post('email'), FILTER_VALIDATE_EMAIL) === false) {
return new JsonContent(['error' => ['errorText' => 'The given email address is not valid.']]);
}
@@ -455,6 +468,18 @@ class LoginController
]);
}
if (!empty($_ENV['RECAPTCHA_SITEKEY'])) {
if (!$this->request->post('g-recaptcha-response')) {
return new JsonContent(['error' => ['errorText' => 'Please check "I\'m not a robot" in the reCAPTCHA box!']]);
}
$captchaValidator = new CaptchaValidator();
$captchaResponse = $captchaValidator->validate($this->request->post('g-recaptcha-response'));
if (!$captchaResponse['success']) {
return new JsonContent(['error' => ['errorText' => 'reCAPTCHA challenge failed. Please try again!']]);
}
}
$user = $this->userRepository->getByEmail($this->request->post('email'));
if ($user === null) {
+4
View File
@@ -81,6 +81,7 @@ class MapAdminController implements ISecured
'mapId' => $mapId,
'mapName' => $map->getName(),
'mapDescription' => str_replace('<br>', "\n", $map->getDescription()),
'mapUnlisted' => $map->getUnlisted(),
'bounds' => $map->getBounds()->toArray(),
'places' => &$places
]);
@@ -175,6 +176,9 @@ class MapAdminController implements ISecured
if (isset($_POST['description'])) {
$map->setDescription(str_replace(["\n", "\r\n"], '<br>', $_POST['description']));
}
if (isset($_POST['unlisted'])) {
$map->setUnlisted((bool)$_POST['unlisted']);
}
$this->pdm->saveToDb($map);
+8 -2
View File
@@ -30,12 +30,19 @@ class MapsController
['maps', 'bound_north_lat'],
['maps', 'bound_east_lng'],
['maps', 'area'],
['maps', 'unlisted'],
new RawExpression('COUNT(places.id) AS num_places')
]);
$select->leftJoin('places', ['places', 'map_id'], '=', ['maps', 'id']);
$select->groupBy(['maps', 'id']);
$select->orderBy('name');
$user = $this->request->user();
$isAdmin = $user !== null && $user->hasPermission(IUser::PERMISSION_ADMIN);
if (!$isAdmin) {
$select->where(['maps', 'unlisted'], '=', false);
}
$result = $select->execute();
$maps = [];
@@ -45,11 +52,10 @@ class MapsController
$maps[] = $map;
}
$user = $this->request->user();
return new HtmlContent('maps', [
'maps' => $maps,
'isLoggedIn' => $user !== null,
'isAdmin' => $user !== null && $user->hasPermission(IUser::PERMISSION_ADMIN)
'isAdmin' => $isAdmin
]);
}
+13 -1
View File
@@ -6,7 +6,7 @@ class Map extends Model
{
protected static string $table = 'maps';
protected static array $fields = ['name', 'description', 'bound_south_lat', 'bound_west_lng', 'bound_north_lat', 'bound_east_lng', 'area'];
protected static array $fields = ['name', 'description', 'bound_south_lat', 'bound_west_lng', 'bound_north_lat', 'bound_east_lng', 'area', 'unlisted'];
private string $name = '';
@@ -16,6 +16,8 @@ class Map extends Model
private float $area = 0.0;
private bool $unlisted = false;
public function __construct()
{
$this->bounds = Bounds::createDirectly(-90.0, -180.0, 90.0, 180.0);
@@ -61,6 +63,11 @@ class Map extends Model
$this->area = $area;
}
public function setUnlisted(bool $unlisted): void
{
$this->unlisted = $unlisted;
}
public function getName(): string
{
return $this->name;
@@ -100,4 +107,9 @@ class Map extends Model
{
return $this->area;
}
public function getUnlisted(): bool
{
return $this->unlisted;
}
}
+19
View File
@@ -0,0 +1,19 @@
<?php namespace MapGuesser\Util;
use MapGuesser\Http\Request;
class CaptchaValidator
{
public function validate(string $response)
{
$request = new Request('https://www.google.com/recaptcha/api/siteverify', Request::HTTP_GET);
$request->setQuery([
'secret' => $_ENV['RECAPTCHA_SECRET'],
'response' => $response
]);
$response = $request->send();
return json_decode($response->getBody(), true);
}
}
+6 -6
View File
@@ -9,24 +9,24 @@
<?php if ($user['password'] !== null && $user['google_sub'] !== null): ?>
<p class="justify small">Please confirm your identity with your password or with Google to modify your account.</p>
<div class="inputWithButton">
<input type="password" name="password" placeholder="Current password" required minlength="6" autofocus><!--
<input type="password" class="text name="password" placeholder="Current password" required minlength="6" autofocus><!--
--><button id="authenticateWithGoogleButton" class="yellow" type="button">Google</button>
</div>
<?php elseif ($user['password'] !== null): ?>
<p class="justify small">Please confirm your identity with your password to modify your account.</p>
<input class="big fullWidth" type="password" name="password" placeholder="Current password" required minlength="6" autofocus>
<input type="password" class="text big fullWidth" name="password" placeholder="Current password" required minlength="6" autofocus>
<?php elseif ($user['google_sub'] !== null): ?>
<p class="justify small">Please confirm your identity with Google to modify your account.</p>
<div class="inputWithButton">
<input type="text" name="password" placeholder="Authenticate with Google..." disabled><!--
<input type="text" class="text" name="password" placeholder="Authenticate with Google..." disabled><!--
--><button id="authenticateWithGoogleButton" class="yellow" type="button">Google</button>
</div>
<?php endif; ?>
<hr>
<?php /* TODO: disabled for the time being, email modification should be implemented */ ?>
<input class="big fullWidth" type="email" name="email" placeholder="Email address" value="<?= $user['email'] ?>" disabled>
<input class="big fullWidth marginTop" type="password" name="password_new" placeholder="New password" minlength="6">
<input class="big fullWidth marginTop" type="password" name="password_new_confirm" placeholder="New password confirmation" minlength="6">
<input type="email" class="text big fullWidth" name="email" placeholder="Email address" value="<?= $user['email'] ?>" disabled>
<input type="password" class="text big fullWidth marginTop" name="password_new" placeholder="New password" minlength="6">
<input type="password" class="text big fullWidth marginTop" name="password_new_confirm" placeholder="New password confirmation" minlength="6">
<p id="accountFormError" class="formError justify marginTop"></p>
<div class="right marginTop">
<button type="submit" name="submit" disabled>Save</button>
+3 -3
View File
@@ -10,16 +10,16 @@
<?php if ($user['password'] !== null && $user['google_sub'] !== null): ?>
<p class="justify small">Please confirm your identity with your password or with Google to delete your account.</p>
<div class="inputWithButton">
<input type="password" name="password" placeholder="Current password" required minlength="6" autofocus><!--
<input type="password" class="text" name="password" placeholder="Current password" required minlength="6" autofocus><!--
--><button id="authenticateWithGoogleButton" class="yellow" type="button">Google</button>
</div>
<?php elseif ($user['password'] !== null): ?>
<p class="justify small">Please confirm your identity with your password to delete your account.</p>
<input class="big fullWidth" type="password" name="password" placeholder="Current password" required minlength="6" autofocus>
<input type="password" class="text big fullWidth" name="password" placeholder="Current password" required minlength="6" autofocus>
<?php elseif ($user['google_sub'] !== null): ?>
<p class="justify small">Please confirm your identity with Google to delete your account.</p>
<div class="inputWithButton">
<input type="text" name="password" placeholder="Authenticate with Google..." disabled><!--
<input type="text" class="text" name="password" placeholder="Authenticate with Google..." disabled><!--
--><button id="authenticateWithGoogleButton" class="yellow" type="button">Google</button>
</div>
<?php endif; ?>
+5 -2
View File
@@ -19,7 +19,7 @@
</button>
</span><!--
--><span class="inline hideOnMobile">
<input type="text" id="jumpCoordinates" placeholder="Insert coordinates here" />
<input type="text" class="text" id="jumpCoordinates" placeholder="Insert coordinates here" />
<button id="jumpButton" disabled >Jump</button>
</span><!--
--><span><!--
@@ -52,8 +52,11 @@
<div id="metadata" class="modal">
<h2>Edit map data</h2>
<form id="metadataForm" class="marginTop" data-no-submit="true">
<input class="fullWidth" type="text" name="name" value="<?= $mapName ?>" placeholder="Name of the map">
<input type="text" class="text fullWidth" name="name" value="<?= $mapName ?>" placeholder="Name of the map">
<textarea class="fullWidth marginTop" name="description" rows="4" placeholder="Description of the map"><?= $mapDescription ?></textarea>
<div class="marginTop">
<input type="checkbox" id="unlisted" name="unlisted" <?= $mapUnlisted ? 'checked' : '' ?>><label for="unlisted">Unlisted</label>
</div>
<div class="right">
<button class="marginTop marginRight" type="submit">Apply</button><!--
--><button id="closeMetadataButton" class="gray marginTop" type="button">Close</button>
+1 -1
View File
@@ -11,7 +11,7 @@
<?php else: ?>
<p class="justify">Please confirm your sign up request. Your account will be linked to your Google account.</p>
<?php endif; ?>
<input class="big fullWidth marginTop" type="email" name="email" placeholder="Email address" value="<?= $email ?>" disabled>
<input type="email" class="text big fullWidth marginTop" name="email" placeholder="Email address" value="<?= $email ?>" disabled>
<div class="right">
<button class="marginTop marginRight" type="submit">
<?php if ($found): ?>
+2 -2
View File
@@ -4,8 +4,8 @@
<h2>Login</h2>
<div class="box">
<form id="loginForm" action="/login" method="post" data-redirect-on-success="/">
<input class="big fullWidth" type="email" name="email" placeholder="Email address" required autofocus>
<input class="big fullWidth marginTop" type="password" name="password" placeholder="Password" required minlength="6">
<input type="email" class="text big fullWidth" name="email" placeholder="Email address" required autofocus>
<input type="password" class="text big fullWidth marginTop" name="password" placeholder="Password" required minlength="6">
<p id="loginFormError" class="formError justify marginTop"></p>
<div class="right marginTop">
<button type="submit">Login</button>
+8 -1
View File
@@ -1,10 +1,17 @@
@js(https://www.google.com/recaptcha/api.js)
@extends(templates/layout_normal)
@section(main)
<h2>Request password reset</h2>
<div class="box">
<form id="passwordResetForm" action="/password/requestReset" method="post" data-redirect-on-success="/password/requestReset/success">
<input class="big fullWidth" type="email" name="email" placeholder="Email address" value="<?= isset($email) ? $email : '' ?>" required autofocus>
<input type="email" class="text big fullWidth" name="email" placeholder="Email address" value="<?= isset($email) ? $email : '' ?>" required autofocus>
<?php if (!empty($_ENV['RECAPTCHA_SITEKEY'])): ?>
<div class="marginTop">
<div class="g-recaptcha" data-sitekey="<?= $_ENV['RECAPTCHA_SITEKEY'] ?>"></div>
</div>
<?php endif; ?>
<p id="passwordResetFormError" class="formError justify marginTop"></p>
<div class="right marginTop">
<button type="submit">Continue</button>
+3 -3
View File
@@ -5,9 +5,9 @@
<div class="box">
<?php if ($success) : ?>
<form id="resetPasswordForm" action="/password/reset/<?= $token ?>" method="post" data-redirect-on-success="/">
<input class="big fullWidth" type="email" name="email" placeholder="Email address" value="<?= $email ?>" disabled>
<input class="big fullWidth marginTop" type="password" name="password" placeholder="Password" required minlength="6" autofocus>
<input class="big fullWidth marginTop" type="password" name="password_confirm" placeholder="Password confirmation" required minlength="6">
<input type="email" class="text big fullWidth" name="email" placeholder="Email address" value="<?= $email ?>" disabled>
<input type="password" class="text big fullWidth marginTop" name="password" placeholder="Password" required minlength="6" autofocus>
<input type="password" class="text big fullWidth marginTop" name="password_confirm" placeholder="Password confirmation" required minlength="6">
<p id="resetPasswordFormError" class="formError justify marginTop"></p>
<div class="right">
<button class="marginTop" type="submit">Reset password</button>
+11 -5
View File
@@ -1,3 +1,4 @@
@js(https://www.google.com/recaptcha/api.js)
@js(js/login/signup.js)
@extends(templates/layout_normal)
@@ -8,12 +9,17 @@
<form id="signupForm" action="/signup" method="post" data-redirect-on-success="/signup/success">
<?php if (isset($email)): ?>
<p class="justify">No user found with the given email address. Sign up with one click!</p>
<input class="big fullWidth marginTop" type="email" name="email" placeholder="Email address" value="<?= $email ?>" required>
<input class="big fullWidth marginTop" type="password" name="password" placeholder="Password confirmation" required minlength="6" autofocus>
<input type="email" class="text big fullWidth marginTop" name="email" placeholder="Email address" value="<?= $email ?>" required>
<input type="password" class="text big fullWidth marginTop" name="password" placeholder="Password confirmation" required minlength="6" autofocus>
<?php else: ?>
<input class="big fullWidth" type="email" name="email" placeholder="Email address" required autofocus>
<input class="big fullWidth marginTop" type="password" name="password" placeholder="Password" required minlength="6">
<input class="big fullWidth marginTop" type="password" name="password_confirm" placeholder="Password confirmation" minlength="6">
<input type="email" class="text big fullWidth" name="email" placeholder="Email address" required autofocus>
<input type="password" class="text big fullWidth marginTop" name="password" placeholder="Password" required minlength="6">
<input type="password" class="text big fullWidth marginTop" name="password_confirm" placeholder="Password confirmation" minlength="6">
<?php endif; ?>
<?php if (!empty($_ENV['RECAPTCHA_SITEKEY'])): ?>
<div class="marginTop">
<div class="g-recaptcha" data-sitekey="<?= $_ENV['RECAPTCHA_SITEKEY'] ?>"></div>
</div>
<?php endif; ?>
<p id="signupFormError" class="formError justify marginTop"></p>
<div class="right">
+13 -31
View File
@@ -25,7 +25,7 @@ TODO: condition!
<a id="createNewRoomButton" class="button fullWidth green" href="" title="Create new room">Create new room</a>
<p class="bold center marginTop marginBottom">OR</p>
<div class="inputWithButton">
<input type="text" name="roomId" placeholder="Room to connect" required minlength="6" maxlength="6"><!--
<input type="text" class="text" name="roomId" placeholder="Room to connect" required minlength="6" maxlength="6"><!--
--><button id="authenticateWithGoogleButton" type="submit">Connect</button>
</div>
<div class="right">
@@ -36,42 +36,24 @@ TODO: condition!
<div id="challenge" class="modal">
<h2>Challenge (gamma)</h2>
<form id="challengeForm" class="marginTop" method="get">
<!--
<div class="inputWithButton">
<input type="text" name="challengeToken" placeholder="Challenge to enter" required minlength="6" maxlength="6">
</div>
<p class="bold center marginTop marginBottom">OR</p>
-->
<div id="restrictions" class="marginTop marginBottom">
<h3>Optional restrictions</h3>
<div>
<div>
<input type="checkbox" id="timerEnabled" name="timerEnabled" value="timerEnabled" />
<label id="timeLimitLabel" for="timerEnabled">Time limit measured in seconds</label>
</div>
<div>
<input type="range" id="timeLimit" name="timeLimit" min="10" max="1800" step="10" value="300" />
<div class="marginTop">
<input type="checkbox" id="timerEnabled" name="timerEnabled" value="timerEnabled" /><label id="timeLimitLabel" for="timerEnabled">Time limit measured in seconds</label>
</div>
<div id="timeLimitType">
<label>Time limit</label>
<input type="radio" id="timeLimitTypeGame" name="timeLimitType" value="game" checked />
<label for="timeLimitTypeGame">for the whole game</label>
<input type="radio" id="timeLimitTypeRound" name="timeLimitType" value="round" />
<label for="timeLimitTypeRound">per round</label>
<input class="fullWidth" type="range" id="timeLimit" name="timeLimit" min="10" max="1800" step="10" value="300" />
<input type="radio" id="timeLimitTypeGame" name="timeLimitType" value="game" checked /><label for="timeLimitTypeGame">for the whole game</label>
<input class="marginLeft" type="radio" id="timeLimitTypeRound" name="timeLimitType" value="round" /><label for="timeLimitTypeRound">per round</label>
</div>
<div class="marginTop">
<input type="checkbox" id="noMove" name="noMove" value="noMove" /><label for="noMove">No movement allowed</label>
</div>
<div>
<input type="checkbox" id="noMove" name="noMove" value="noMove" />
<label for="noMove">No movement allowed</label>
<div class="marginTop">
<input type="checkbox" id="noZoom" name="noZoom" value="noZoom" /><label for="noZoom">No zoom allowed</label>
</div>
<div>
<input type="checkbox" id="noZoom" name="noZoom" value="noZoom" />
<label for="noZoom">No zoom allowed</label>
</div>
<div>
<input type="checkbox" id="noPan" name="noPan" value="noPan" />
<label for="noPan">No camera change allowed</label>
<div class="marginTop">
<input type="checkbox" id="noPan" name="noPan" value="noPan" /><label for="noPan">No camera change allowed</label>
</div>
<input type="hidden" name="mapId" id="challengeMapId" />
</div>
@@ -86,7 +68,7 @@ TODO: condition!
@section(main)
<div id="mapContainer">
<?php foreach ($maps as $map): ?>
<div class="mapItem">
<div class="mapItem <?= $map['unlisted'] ? 'unlisted' : '' ?>">
<div class="title">
<p class="title"><?= $map['name'] ?></p>
</div>