-
Notifications
You must be signed in to change notification settings - Fork 395
Feature: Proof of Work captcha #4495
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: dev
Are you sure you want to change the base?
Changes from 17 commits
0b2b78c
b3b6c80
6fc0381
356676b
35a4318
5a6f033
843c2ea
a21c1e2
1d3336e
5746da2
136e3cb
04fe429
6b5884f
8ff7271
761e780
c97bfba
8dc7d17
62ca158
d06cb50
7f1b88a
182f5bd
a9f9003
a358335
d6972fa
fb11bf4
de5dae0
826c5a9
2e485c6
22e93c6
8723bef
e61d683
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,110 @@ | ||
| <?php | ||
|
|
||
| /** | ||
| * Altcha proof-of-work CAPTCHA. | ||
| * | ||
| * PHP version 8 | ||
| * | ||
| * Copyright (C) Villanova University 2025. | ||
| * | ||
| * This program is free software; you can redistribute it and/or modify | ||
| * it under the terms of the GNU General Public License version 2, | ||
| * as published by the Free Software Foundation. | ||
| * | ||
| * This program is distributed in the hope that it will be useful, | ||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| * GNU General Public License for more details. | ||
| * | ||
| * You should have received a copy of the GNU General Public License | ||
| * along with this program; if not, write to the Free Software | ||
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
| * | ||
| * @category VuFind | ||
| * @package CAPTCHA | ||
| * @author Chris Hallberg <challber@villanova.edu> | ||
| * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License | ||
| * @link https://vufind.org Main Page | ||
| */ | ||
|
|
||
| namespace VuFind\Captcha; | ||
|
|
||
| use AltchaOrg\Altcha\ChallengeOptions; | ||
| use AltchaOrg\Altcha\Hasher\Algorithm; | ||
| use Laminas\Mvc\Controller\Plugin\Params; | ||
|
|
||
| /** | ||
| * Altcha proof-of-work CAPTCHA. | ||
| * | ||
| * @category VuFind | ||
| * @package CAPTCHA | ||
| * @author Chris Hallberg <challber@villanova.edu> | ||
| * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License | ||
| * @link https://vufind.org/wiki/development Wiki | ||
| */ | ||
| class Altcha extends AbstractBase | ||
| { | ||
| /** | ||
| * Constructor | ||
| * | ||
| * @param AltchaOrg\Altcha\Altcha $altcha Required HMAC key for challenge calculation and solution verification. | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This comment needs to be updated to reflect the new value (and aligned with the values below). |
||
| * @param Algorithm $algorithm Hashing algorithm to use (`SHA-1`, `SHA-256`, `SHA-512`, default: `SHA-256`). | ||
| * @param int $maxNumber Maximum number for the random number generator (default: 1,000,000) | ||
| * @param null|\DateTimeInterface $expires Optional expiration time for the challenge. | ||
| * @param ChallengeParams $params Optional URL-encoded query parameters. | ||
| * @param int<1, max> $saltLength Length of the random salt (default: 12 bytes). | ||
| */ | ||
| public function __construct( | ||
| protected AltchaOrg\Altcha\Altcha $altcha, | ||
| // Options for creation of a new challenge | ||
| protected Algorithm $algorithm = Algorithm::SHA256, | ||
| protected ?\DateTimeInterface $expires = null, | ||
| protected array $params = [], | ||
| ) { | ||
| } | ||
|
|
||
| /** | ||
| * Get list of URLs with JS dependencies to load for the active CAPTCHA type. | ||
| * | ||
| * @return array | ||
| */ | ||
| public function getJsIncludes(): array | ||
| { | ||
| return ['vendor/altcha.js', 'vendor/altcha-i18n.js']; | ||
| } | ||
|
|
||
| /** | ||
| * Generate challenge | ||
| * | ||
| * @return Challenge | ||
| */ | ||
| public function getChallenge() | ||
| { | ||
| $options = new ChallengeOptions( | ||
| algorithm: $this->algorithm, | ||
| maxNumber: $this->maxNumber, | ||
| expires: $this->expires, | ||
| params: $this->params, | ||
| saltLength: $this->saltLength, | ||
| ); | ||
|
|
||
| return json_encode($this->altcha->createChallenge($options)); | ||
| } | ||
|
|
||
| /** | ||
| * Pull the captcha field from controller params and check them for accuracy | ||
| * We pull from the form in case config changed since challenge was sent | ||
| * | ||
| * @param Params $params Controller params | ||
| * | ||
| * @return bool | ||
| */ | ||
| public function verify(Params $params): bool | ||
| { | ||
| $encoded = $params->fromPost('altcha', null); | ||
| $json = base64_decode($encoded); | ||
| $payload = json_decode($json, true); | ||
|
|
||
| return $this->altcha->verifySolution($payload, checkExpires: true); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,105 @@ | ||||||||||
| <?php | ||||||||||
|
|
||||||||||
| /** | ||||||||||
| * Factory for Altcha proof-of-work CAPTCHA module. | ||||||||||
| * | ||||||||||
| * PHP version 8 | ||||||||||
| * | ||||||||||
| * Copyright (C) Villanova University 2025. | ||||||||||
| * | ||||||||||
| * This program is free software; you can redistribute it and/or modify | ||||||||||
| * it under the terms of the GNU General Public License version 2, | ||||||||||
| * as published by the Free Software Foundation. | ||||||||||
| * | ||||||||||
| * This program is distributed in the hope that it will be useful, | ||||||||||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||||||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||||||||
| * GNU General Public License for more details. | ||||||||||
| * | ||||||||||
| * You should have received a copy of the GNU General Public License | ||||||||||
| * along with this program; if not, write to the Free Software | ||||||||||
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||||||||||
| * | ||||||||||
| * @category VuFind | ||||||||||
| * @package CAPTCHA | ||||||||||
| * @author Chris Hallberg <challber@villanova.edu> | ||||||||||
| * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License | ||||||||||
| * @link https://vufind.org/wiki/development Wiki | ||||||||||
| */ | ||||||||||
|
|
||||||||||
| namespace VuFind\Captcha; | ||||||||||
|
|
||||||||||
| use AltchaOrg\Altcha\Hasher\Algorithm; | ||||||||||
| use Laminas\ServiceManager\Exception\ServiceNotCreatedException; | ||||||||||
| use Laminas\ServiceManager\Exception\ServiceNotFoundException; | ||||||||||
| use Laminas\ServiceManager\Factory\FactoryInterface; | ||||||||||
| use Psr\Container\ContainerExceptionInterface as ContainerException; | ||||||||||
| use Psr\Container\ContainerInterface; | ||||||||||
|
|
||||||||||
| /** | ||||||||||
| * Altcha proof-of-work CAPTCHA factory. | ||||||||||
| * | ||||||||||
| * @category VuFind | ||||||||||
| * @package CAPTCHA | ||||||||||
| * @author Chris Hallberg <challber@villanova.edu> | ||||||||||
| * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License | ||||||||||
| * @link https://vufind.org/wiki/development Wiki | ||||||||||
| */ | ||||||||||
| class AltchaFactory implements FactoryInterface | ||||||||||
| { | ||||||||||
| /** | ||||||||||
| * Create an object | ||||||||||
| * | ||||||||||
| * @param ContainerInterface $container Service manager | ||||||||||
| * @param string $requestedName Service being created | ||||||||||
| * @param null|array $options Extra options (optional) | ||||||||||
| * | ||||||||||
| * @return object | ||||||||||
| * | ||||||||||
| * @throws ServiceNotFoundException if unable to resolve the service. | ||||||||||
| * @throws ServiceNotCreatedException if an exception is raised when | ||||||||||
| * creating a service. | ||||||||||
| * @throws ContainerException&\Throwable if any other error occurs | ||||||||||
| */ | ||||||||||
| public function __invoke( | ||||||||||
| ContainerInterface $container, | ||||||||||
| $requestedName, | ||||||||||
| ?array $options = null | ||||||||||
| ) { | ||||||||||
| if (!empty($options)) { | ||||||||||
| throw new \Exception('Unexpected options passed to factory.'); | ||||||||||
| } | ||||||||||
|
|
||||||||||
| $config = $container | ||||||||||
| ->get(\VuFind\Config\PluginManager::class) | ||||||||||
| ->get('config'); | ||||||||||
|
Comment on lines
+74
to
+75
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. \VuFind\Config\PluginManager has been deprecated. This should become:
Suggested change
|
||||||||||
|
|
||||||||||
| $secret = $config->Captcha->altcha_secret ?? null; | ||||||||||
|
|
||||||||||
| if (empty($secret)) { | ||||||||||
| throw new \Exception('Secret key needed for Altcha. See config.ini.'); | ||||||||||
| } | ||||||||||
|
|
||||||||||
| $algorithm = Algorithm::from($config->Captcha->altcha_algorithm ?? 'SHA-256'); | ||||||||||
|
crhallberg marked this conversation as resolved.
Outdated
|
||||||||||
| $max_number = $config->Captcha->altcha_max_number ?? 100000; | ||||||||||
| $salt_len = $config->Captcha->altcha_salt_len ?? 12; | ||||||||||
| $expires_interval = $config->Captcha->altcha_expires_interval ?? null; | ||||||||||
| $params = $config->Captcha->altcha_params ?? []; | ||||||||||
|
|
||||||||||
| $expires = !empty($expires_interval) | ||||||||||
| ? (new \DateTimeImmutable())->add(new \DateInterval($expires_interval)) | ||||||||||
| : null; | ||||||||||
|
|
||||||||||
| $altcha = new AltchaOrg\Altcha\Altcha($secret); | ||||||||||
|
|
||||||||||
| return new $requestedName( | ||||||||||
| $altcha, | ||||||||||
| // challenge options | ||||||||||
| $algorithm, | ||||||||||
| $max_number, | ||||||||||
| $expires, | ||||||||||
| $params, | ||||||||||
| $salt_len, | ||||||||||
| ); | ||||||||||
| } | ||||||||||
| } | ||||||||||
Uh oh!
There was an error while loading. Please reload this page.