Captcha trait
This commit is contained in:
parent
3a639d9a3c
commit
76cd92ee0e
30
Core/API/Traits/Captcha.trait.php
Normal file
30
Core/API/Traits/Captcha.trait.php
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Core\API\Traits;
|
||||||
|
|
||||||
|
use Core\API\Parameter\StringType;
|
||||||
|
use Core\API\VerifyCaptcha;
|
||||||
|
use Core\Objects\Context;
|
||||||
|
|
||||||
|
trait Captcha {
|
||||||
|
|
||||||
|
function addCaptchaParameters(array &$parameters): void {
|
||||||
|
$settings = $this->context->getSettings();
|
||||||
|
if ($settings->isCaptchaEnabled()) {
|
||||||
|
$parameters["captcha"] = new StringType("captcha");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkCaptcha(string $action): bool {
|
||||||
|
$settings = $this->context->getSettings();
|
||||||
|
if ($settings->isCaptchaEnabled()) {
|
||||||
|
$captcha = $this->getParam("captcha");
|
||||||
|
$req = new VerifyCaptcha($this->context);
|
||||||
|
if (!$req->execute(array("captcha" => $captcha, "action" => $action))) {
|
||||||
|
return $this->createError($req->getLastError());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
@ -124,9 +124,9 @@ namespace Core\API\User {
|
|||||||
use Core\API\Parameter\Parameter;
|
use Core\API\Parameter\Parameter;
|
||||||
use Core\API\Parameter\StringType;
|
use Core\API\Parameter\StringType;
|
||||||
use Core\API\Template\Render;
|
use Core\API\Template\Render;
|
||||||
|
use Core\API\Traits\Captcha;
|
||||||
use Core\API\Traits\Pagination;
|
use Core\API\Traits\Pagination;
|
||||||
use Core\API\UserAPI;
|
use Core\API\UserAPI;
|
||||||
use Core\API\VerifyCaptcha;
|
|
||||||
use Core\Driver\SQL\Condition\CondBool;
|
use Core\Driver\SQL\Condition\CondBool;
|
||||||
use Core\Driver\SQL\Condition\CondLike;
|
use Core\Driver\SQL\Condition\CondLike;
|
||||||
use Core\Driver\SQL\Condition\CondOr;
|
use Core\Driver\SQL\Condition\CondOr;
|
||||||
@ -716,6 +716,8 @@ namespace Core\API\User {
|
|||||||
|
|
||||||
class Register extends UserAPI {
|
class Register extends UserAPI {
|
||||||
|
|
||||||
|
use Captcha;
|
||||||
|
|
||||||
public function __construct(Context $context, bool $externalCall = false) {
|
public function __construct(Context $context, bool $externalCall = false) {
|
||||||
$parameters = array(
|
$parameters = array(
|
||||||
"username" => new StringType("username", 32),
|
"username" => new StringType("username", 32),
|
||||||
@ -724,10 +726,7 @@ namespace Core\API\User {
|
|||||||
"confirmPassword" => new StringType("confirmPassword"),
|
"confirmPassword" => new StringType("confirmPassword"),
|
||||||
);
|
);
|
||||||
|
|
||||||
$settings = $context->getSettings();
|
$this->addCaptchaParameters($parameters);
|
||||||
if ($settings->isCaptchaEnabled()) {
|
|
||||||
$parameters["captcha"] = new StringType("captcha");
|
|
||||||
}
|
|
||||||
|
|
||||||
parent::__construct($context, $externalCall, $parameters);
|
parent::__construct($context, $externalCall, $parameters);
|
||||||
$this->csrfTokenRequired = false;
|
$this->csrfTokenRequired = false;
|
||||||
@ -745,12 +744,8 @@ namespace Core\API\User {
|
|||||||
return $this->createError("User Registration is not enabled.");
|
return $this->createError("User Registration is not enabled.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($settings->isCaptchaEnabled()) {
|
if (!$this->checkCaptcha("register")) {
|
||||||
$captcha = $this->getParam("captcha");
|
return false;
|
||||||
$req = new VerifyCaptcha($this->context);
|
|
||||||
if (!$req->execute(array("captcha" => $captcha, "action" => "register"))) {
|
|
||||||
return $this->createError($req->getLastError());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$username = $this->getParam("username");
|
$username = $this->getParam("username");
|
||||||
@ -840,7 +835,8 @@ namespace Core\API\User {
|
|||||||
'email' => new Parameter('email', Parameter::TYPE_EMAIL, true, NULL),
|
'email' => new Parameter('email', Parameter::TYPE_EMAIL, true, NULL),
|
||||||
'password' => new StringType('password', -1, true, NULL),
|
'password' => new StringType('password', -1, true, NULL),
|
||||||
'groups' => new ArrayType('groups', Parameter::TYPE_INT, true, true, NULL),
|
'groups' => new ArrayType('groups', Parameter::TYPE_INT, true, true, NULL),
|
||||||
'confirmed' => new Parameter('confirmed', Parameter::TYPE_BOOLEAN, true, NULL)
|
'confirmed' => new Parameter('confirmed', Parameter::TYPE_BOOLEAN, true, NULL),
|
||||||
|
'active' => new Parameter('active', Parameter::TYPE_BOOLEAN, true, NULL)
|
||||||
));
|
));
|
||||||
|
|
||||||
$this->loginRequired = true;
|
$this->loginRequired = true;
|
||||||
@ -865,6 +861,7 @@ namespace Core\API\User {
|
|||||||
$password = $this->getParam("password");
|
$password = $this->getParam("password");
|
||||||
$groups = $this->getParam("groups");
|
$groups = $this->getParam("groups");
|
||||||
$confirmed = $this->getParam("confirmed");
|
$confirmed = $this->getParam("confirmed");
|
||||||
|
$active = $this->getParam("active");
|
||||||
|
|
||||||
$email = (!is_null($email) && empty($email)) ? null : $email;
|
$email = (!is_null($email) && empty($email)) ? null : $email;
|
||||||
|
|
||||||
@ -918,13 +915,22 @@ namespace Core\API\User {
|
|||||||
|
|
||||||
if (!is_null($confirmed)) {
|
if (!is_null($confirmed)) {
|
||||||
if ($id === $currentUser->getId() && $confirmed === false) {
|
if ($id === $currentUser->getId() && $confirmed === false) {
|
||||||
return $this->createError("Cannot make own account unconfirmed.");
|
return $this->createError("Cannot change confirmed flag on own account.");
|
||||||
} else {
|
} else {
|
||||||
$user->confirmed = $confirmed;
|
$user->confirmed = $confirmed;
|
||||||
$columnsToUpdate[] = "confirmed";
|
$columnsToUpdate[] = "confirmed";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!is_null($active)) {
|
||||||
|
if ($id === $currentUser->getId() && $active === false) {
|
||||||
|
return $this->createError("Cannot change active flag on own account.");
|
||||||
|
} else {
|
||||||
|
$user->active = $active;
|
||||||
|
$columnsToUpdate[] = "active";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (empty($columnsToUpdate) || $user->save($sql, $columnsToUpdate)) {
|
if (empty($columnsToUpdate) || $user->save($sql, $columnsToUpdate)) {
|
||||||
|
|
||||||
$deleteQuery = $sql->delete("UserGroup")->whereEq("user_id", $id);
|
$deleteQuery = $sql->delete("UserGroup")->whereEq("user_id", $id);
|
||||||
@ -995,16 +1001,15 @@ namespace Core\API\User {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class RequestPasswordReset extends UserAPI {
|
class RequestPasswordReset extends UserAPI {
|
||||||
|
|
||||||
|
use Captcha;
|
||||||
|
|
||||||
public function __construct(Context $context, $externalCall = false) {
|
public function __construct(Context $context, $externalCall = false) {
|
||||||
$parameters = array(
|
$parameters = [
|
||||||
'email' => new Parameter('email', Parameter::TYPE_EMAIL),
|
'email' => new Parameter('email', Parameter::TYPE_EMAIL),
|
||||||
);
|
];
|
||||||
|
|
||||||
$settings = $context->getSettings();
|
|
||||||
if ($settings->isCaptchaEnabled()) {
|
|
||||||
$parameters["captcha"] = new StringType("captcha");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
$this->addCaptchaParameters($parameters);
|
||||||
parent::__construct($context, $externalCall, $parameters);
|
parent::__construct($context, $externalCall, $parameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1019,12 +1024,8 @@ namespace Core\API\User {
|
|||||||
return $this->createError("The mail service is not enabled, please contact the server administration.");
|
return $this->createError("The mail service is not enabled, please contact the server administration.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($settings->isCaptchaEnabled()) {
|
if (!$this->checkCaptcha("resetPassword")) {
|
||||||
$captcha = $this->getParam("captcha");
|
return false;
|
||||||
$req = new VerifyCaptcha($this->context);
|
|
||||||
if (!$req->execute(array("captcha" => $captcha, "action" => "resetPassword"))) {
|
|
||||||
return $this->createError($req->getLastError());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$sql = $this->context->getSQL();
|
$sql = $this->context->getSQL();
|
||||||
@ -1088,16 +1089,15 @@ namespace Core\API\User {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class ResendConfirmEmail extends UserAPI {
|
class ResendConfirmEmail extends UserAPI {
|
||||||
|
|
||||||
|
use Captcha;
|
||||||
|
|
||||||
public function __construct(Context $context, $externalCall = false) {
|
public function __construct(Context $context, $externalCall = false) {
|
||||||
$parameters = array(
|
$parameters = array(
|
||||||
'email' => new Parameter('email', Parameter::TYPE_EMAIL),
|
'email' => new Parameter('email', Parameter::TYPE_EMAIL),
|
||||||
);
|
);
|
||||||
|
|
||||||
$settings = $context->getSettings();
|
$this->addCaptchaParameters($parameters);
|
||||||
if ($settings->isCaptchaEnabled()) {
|
|
||||||
$parameters["captcha"] = new StringType("captcha");
|
|
||||||
}
|
|
||||||
|
|
||||||
parent::__construct($context, $externalCall, $parameters);
|
parent::__construct($context, $externalCall, $parameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1108,12 +1108,8 @@ namespace Core\API\User {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$settings = $this->context->getSettings();
|
$settings = $this->context->getSettings();
|
||||||
if ($settings->isCaptchaEnabled()) {
|
if (!$this->checkCaptcha("resendConfirmation")) {
|
||||||
$captcha = $this->getParam("captcha");
|
return false;
|
||||||
$req = new VerifyCaptcha($this->context);
|
|
||||||
if (!$req->execute(array("captcha" => $captcha, "action" => "resendConfirmation"))) {
|
|
||||||
return $this->createError($req->getLastError());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$email = $this->getParam("email");
|
$email = $this->getParam("email");
|
||||||
|
@ -9,7 +9,7 @@ return [
|
|||||||
"search" => "Suche",
|
"search" => "Suche",
|
||||||
"search_query" => "Suchanfrage",
|
"search_query" => "Suchanfrage",
|
||||||
"no_entries_placeholder" => "Keine Log-Einträge zum Anzeigen",
|
"no_entries_placeholder" => "Keine Log-Einträge zum Anzeigen",
|
||||||
"timestamp_placeholder" => "Datum und Zeitpunk Auswählen zum Filtern",
|
"timestamp_placeholder" => "Datum und Zeitpunkt auswählen zum Filtern",
|
||||||
"hide_details" => "Details verstecken",
|
"hide_details" => "Details verstecken",
|
||||||
"show_details" => "Details zeigen",
|
"show_details" => "Details zeigen",
|
||||||
|
|
||||||
|
@ -1,15 +1,32 @@
|
|||||||
import {Link, useNavigate, useParams} from "react-router-dom";
|
import {Link, useNavigate, useParams} from "react-router-dom";
|
||||||
import {useCallback, useContext, useEffect, useState} from "react";
|
import {useCallback, useContext, useEffect, useState} from "react";
|
||||||
import {CircularProgress} from "@mui/material";
|
import {
|
||||||
|
Box,
|
||||||
|
Button,
|
||||||
|
Checkbox,
|
||||||
|
CircularProgress,
|
||||||
|
FormControl,
|
||||||
|
FormControlLabel,
|
||||||
|
FormLabel,
|
||||||
|
TextField
|
||||||
|
} from "@mui/material";
|
||||||
import {LocaleContext} from "shared/locale";
|
import {LocaleContext} from "shared/locale";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import ViewContent from "../../elements/view-content";
|
import ViewContent from "../../elements/view-content";
|
||||||
|
import FormGroup from "../../elements/form-group";
|
||||||
|
import ButtonBar from "../../elements/button-bar";
|
||||||
|
import {RestartAlt, Save} from "@mui/icons-material";
|
||||||
|
import {parseBool} from "shared/util";
|
||||||
|
import SpacedFormGroup from "../../elements/form-group";
|
||||||
|
|
||||||
export default function UserEditView(props) {
|
export default function UserEditView(props) {
|
||||||
|
|
||||||
|
// meta
|
||||||
const { api, showDialog } = props;
|
const { api, showDialog } = props;
|
||||||
const { userId } = useParams();
|
const { userId } = useParams();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
// data
|
||||||
const isNewUser = userId === "new";
|
const isNewUser = userId === "new";
|
||||||
const {translate: L, requestModules, currentLocale} = useContext(LocaleContext);
|
const {translate: L, requestModules, currentLocale} = useContext(LocaleContext);
|
||||||
const [fetchUser, setFetchUser] = useState(!isNewUser);
|
const [fetchUser, setFetchUser] = useState(!isNewUser);
|
||||||
@ -20,8 +37,13 @@ export default function UserEditView(props) {
|
|||||||
password: "",
|
password: "",
|
||||||
groups: [],
|
groups: [],
|
||||||
confirmed: false,
|
confirmed: false,
|
||||||
|
active: true,
|
||||||
} : null);
|
} : null);
|
||||||
|
|
||||||
|
// ui
|
||||||
|
const [hasChanged, setChanged] = useState(isNewUser);
|
||||||
|
const [isSaving, setSaving] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
requestModules(props.api, ["general", "account"], currentLocale).then(data => {
|
requestModules(props.api, ["general", "account"], currentLocale).then(data => {
|
||||||
if (!data.success) {
|
if (!data.success) {
|
||||||
@ -30,6 +52,14 @@ export default function UserEditView(props) {
|
|||||||
});
|
});
|
||||||
}, [currentLocale]);
|
}, [currentLocale]);
|
||||||
|
|
||||||
|
const onReset = useCallback(() => {
|
||||||
|
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const onSaveUser = useCallback(() => {
|
||||||
|
|
||||||
|
}, []);
|
||||||
|
|
||||||
const onFetchUser = useCallback((force = false) => {
|
const onFetchUser = useCallback((force = false) => {
|
||||||
if (!isNewUser && (force || fetchUser)) {
|
if (!isNewUser && (force || fetchUser)) {
|
||||||
setFetchUser(false);
|
setFetchUser(false);
|
||||||
@ -46,6 +76,10 @@ export default function UserEditView(props) {
|
|||||||
}
|
}
|
||||||
}, [api, showDialog, fetchUser, isNewUser, userId, user]);
|
}, [api, showDialog, fetchUser, isNewUser, userId, user]);
|
||||||
|
|
||||||
|
const onChangeValue = useCallback((name, value) => {
|
||||||
|
|
||||||
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isNewUser) {
|
if (!isNewUser) {
|
||||||
onFetchUser(true);
|
onFetchUser(true);
|
||||||
@ -61,6 +95,69 @@ export default function UserEditView(props) {
|
|||||||
<Link key={"users"} to={"/admin/users"}>User</Link>,
|
<Link key={"users"} to={"/admin/users"}>User</Link>,
|
||||||
<span key={"action"}>{isNewUser ? "New" : "Edit"}</span>
|
<span key={"action"}>{isNewUser ? "New" : "Edit"}</span>
|
||||||
]}>
|
]}>
|
||||||
|
<Box>
|
||||||
|
<FormGroup>
|
||||||
|
<FormLabel>{L("account.name")}</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<TextField size={"small"} variant={"outlined"}
|
||||||
|
value={user.name}
|
||||||
|
onChange={e => setUser({...user, name: e.target.value})} />
|
||||||
|
</FormControl>
|
||||||
|
</FormGroup>
|
||||||
|
<FormGroup>
|
||||||
|
<FormLabel>{L("account.full_name")}</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<TextField size={"small"} variant={"outlined"}
|
||||||
|
value={user.fullName}
|
||||||
|
onChange={e => setUser({...user, fullName: e.target.value})} />
|
||||||
|
</FormControl>
|
||||||
|
</FormGroup>
|
||||||
|
<FormGroup>
|
||||||
|
<FormLabel>{L("account.email")}</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<TextField size={"small"} variant={"outlined"}
|
||||||
|
value={user.email}
|
||||||
|
type={"email"}
|
||||||
|
onChange={e => setUser({...user, email: e.target.value})} />
|
||||||
|
</FormControl>
|
||||||
|
</FormGroup>
|
||||||
|
{ !isNewUser ?
|
||||||
|
<>
|
||||||
|
<FormGroup>
|
||||||
|
<FormControlLabel
|
||||||
|
control={<Checkbox
|
||||||
|
checked={!!user.active}
|
||||||
|
onChange={(e, v) => onChangeValue("active", v)} />}
|
||||||
|
label={L("account.active")} />
|
||||||
|
</FormGroup>
|
||||||
|
<FormGroup>
|
||||||
|
<FormControlLabel
|
||||||
|
control={<Checkbox
|
||||||
|
checked={!!user.confirmed}
|
||||||
|
onChange={(e, v) => onChangeValue("confirmed", v)} />}
|
||||||
|
label={L("account.confirmed")} />
|
||||||
|
</FormGroup>
|
||||||
|
</> : <>
|
||||||
|
|
||||||
|
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
</Box>
|
||||||
|
<ButtonBar>
|
||||||
|
<Button color={"primary"}
|
||||||
|
onClick={onSaveUser}
|
||||||
|
disabled={isSaving || !(isNewUser ? api.hasPermission("user/create") : api.hasPermission("user/edit"))}
|
||||||
|
startIcon={isSaving ? <CircularProgress size={14} /> : <Save />}
|
||||||
|
variant={"outlined"} title={L(hasChanged ? "general.unsaved_changes" : "general.save")}>
|
||||||
|
{isSaving ? L("general.saving") + "…" : (L("general.save") + (hasChanged ? " *" : ""))}
|
||||||
|
</Button>
|
||||||
|
<Button color={"error"}
|
||||||
|
onClick={onReset}
|
||||||
|
disabled={isSaving}
|
||||||
|
startIcon={<RestartAlt />}
|
||||||
|
variant={"outlined"} title={L("general.reset")}>
|
||||||
|
{L("general.reset")}
|
||||||
|
</Button>
|
||||||
|
</ButtonBar>
|
||||||
</ViewContent>
|
</ViewContent>
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user