Skip to content

Commit

Permalink
refactor captcha: make captcha pluggable (#894)
Browse files Browse the repository at this point in the history
  • Loading branch information
David Coutadeur authored and davidcoutadeur committed Jul 3, 2024
1 parent 7247a0b commit fc2a753
Show file tree
Hide file tree
Showing 28 changed files with 507 additions and 106 deletions.
1 change: 1 addition & 0 deletions conf/config.inc.php
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,7 @@

## Captcha
$use_captcha = false;
$captcha_class = "InternalCaptcha";

## Default action
# change
Expand Down
15 changes: 13 additions & 2 deletions docs/config_general.rst
Original file line number Diff line number Diff line change
Expand Up @@ -268,17 +268,28 @@ GET or POST parameter. This method does not require any configuration.

Example: ``https://ssp.example.com/?actionresetbyquestions&login_hint=spiderman``

.. _config_captcha:

Captcha
-------

To require a captcha, set ``$use_captcha``:
To enable captcha, set ``$use_captcha`` to ``true``.

You should also define the captcha module to use.
(By default, ``InternalCaptcha`` is defined in config.inc.php)

.. code-block:: php
$use_captcha = true;
$captcha_class = "InternalCaptcha";
.. tip:: The captcha is used on every form in Self Service Password
(password change, token, questions, etc.)
(password change, token, questions,...)

For ``$captcha_class``, you can select another captcha module. For now, only ``InternalCaptcha`` and ``FriendlyCaptcha`` are supported.

You can also add your own Captcha module. (see :doc:`developpers` )


.. |image0| image:: images/br.png
.. |image1| image:: images/catalonia.png
Expand Down
160 changes: 160 additions & 0 deletions docs/developpers.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
Developper's corner
===================

LDAP Tool Box Self Service Password can be extended with your own code.

Add your own Captcha system
---------------------------

As presented in :ref:`captcha configuration<config_captcha>`, you can enable a captcha on most of the pages of Self-Service-Password.

You can define a customized class for managing your own captcha class:

.. code-block:: php
$use_captcha = true;
$captcha_class = "MyCustomClass";
Then you have to create the captcha module in ``lib/captcha/MyCustomClass.php``.

Here is a template example of such a captcha module:

.. code-block:: php
<?php namespace captcha;
require_once(__DIR__."/../../vendor/autoload.php");
# use/require any dependency here
class MyCustomClass
{
#private $captcha_property;
public function __construct()
{
#$this->captcha_property = $property;
}
# Function that insert extra css
function generate_css_captcha(){
$captcha_css = '';
return $captcha_css;
}
# Function that insert extra js
function generate_js_captcha(){
$captcha_js = '<script></script>';
return $captcha_js;
}
# Function that generate the html part containing the captcha
function generate_html_captcha($messages){
$captcha_html ='
<div class="row mb-3">
<div class="col-sm-4 col-form-label text-end captcha">
<img src="'.$this->generate_captcha_challenge().'" alt="captcha" />
<i id="captcha-refresh" class="fa fa-fw fa-refresh"></i>
</div>
<div class="col-sm-8">
<div class="input-group">
<span class="input-group-text"><i class="fa fa-fw fa-check-circle"></i></span>
<input type="text" autocomplete="new-password" name="captchaphrase" id="captchaphrase" class="form-control" placeholder="'.$messages["captcha"].'" />
</div>
</div>
</div>';
return $captcha_html;
}
# Function that generate the captcha challenge
# Could be called by the backend, or by a call through a REST API to define
function generate_captcha_challenge(){
# cookie for captcha session
ini_set("session.use_cookies",1);
ini_set("session.use_only_cookies",1);
session_name("captcha");
session_start();
# Generate your captcha challenge here
$challenge = "";
$_SESSION['phrase'] = $challenge;
# session is stored and closed now, used only for captcha
session_write_close();
$captcha_image = $captcha->build()->inline();
return $captcha_image;
}
# Function that verify that the result sent by the user
# matches the captcha challenge
function verify_captcha_challenge(){
$result="";
if (isset($_POST["captchaphrase"]) and $_POST["captchaphrase"]) {
# captcha cookie for session
ini_set("session.use_cookies",1);
ini_set("session.use_only_cookies",1);
setcookie("captcha", '', time()-1000);
session_name("captcha");
session_start();
$captchaphrase = strval($_POST["captchaphrase"]);
# Compare captcha stored in session and user guess
if (! isset($_SESSION['phrase']) or
$_SESSION['phrase'] != $captchaphrase) {
$result = "badcaptcha";
}
unset($_SESSION['phrase']);
# write session to make sure captcha phrase is no more included in session.
session_write_close();
}
else {
$result = "captcharequired";
}
return $result;
}
}
?>
Points of attention:

* you can set any configuration parameters in ``config.inc.local.php``, they will be passed to your class if you define them as properties, and initialize them in the constructor
* you can inject extra css in ``generate_css_captcha`` function
* you can inject extra js in ``generate_js_captcha`` function. For example, js code can useful for refreshing the challenge. If so, you are expected to reach ``/newcaptcha.php`` endpoint. This endpoint would call the ``generate_captcha_challenge`` function in current MyCustomClass and returns the result in json format.
* you must fill in the ``generate_html_captcha`` function. This function must return the html code corresponding to the captcha. It should call the ``generate_captcha_challenge``.
* you must fill in the ``generate_captcha_challenge`` function. This function must generate the challenge, and ensure it is stored somewhere (in the php session). This function can also be called by the REST endpoint: ``/newcaptcha.php``
* you must fill in the ``verify_captcha_challenge`` function. This function must compare the challenge generated and stored, and the user guess. It must return a string corresponding to the status: ``badcaptcha``, ``captcharequired``, or empty string (empty string means challenge is verified)
* don't forget to declare the namespace: ``namespace captcha;``
* don't forget to write the corresponding unit tests (see tests/InternalCaptchaTest.php)


Run unit tests
--------------

Run the unit tests with this command:

```
XDEBUG_MODE=coverage vendor/bin/phpunit --coverage-text --configuration tests/phpunit.xml
```

Take care to use the phpunit shipped with composer.

If you don't have the composer dependencies yet:

```
composer update
```

1 change: 1 addition & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,4 @@ LDAP Tool Box Self Service Password documentation
webservices.rst
audit.rst
set_attributes.rst
developpers.rst
42 changes: 0 additions & 42 deletions htdocs/captcha.php

This file was deleted.

2 changes: 1 addition & 1 deletion htdocs/change.php
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
#==============================================================================
# Check captcha
#==============================================================================
if ( ( $result === "" ) and $use_captcha) { $result = global_captcha_check();}
if ( ( $result === "" ) and $use_captcha) { $result = $captchaInstance->verify_captcha_challenge();}

#==============================================================================
# Check old password
Expand Down
2 changes: 1 addition & 1 deletion htdocs/changecustompwdfield.php
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ function set_default_value(&$variable, $defaultValue)
#==============================================================================
# Check captcha
#==============================================================================
if ( ( $result === "" ) and $use_captcha ) { $result = global_captcha_check();}
if ( ( $result === "" ) and $use_captcha ) { $result = $captchaInstance->verify_captcha_challenge();}

#==============================================================================
# Default configuration
Expand Down
2 changes: 1 addition & 1 deletion htdocs/changesshkey.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@
#==============================================================================
# Check captcha
#==============================================================================
if ( ( $result === "" ) and $use_captcha) { $result = global_captcha_check();}
if ( ( $result === "" ) and $use_captcha) { $result = $captchaInstance->verify_captcha_challenge();}

#==============================================================================
# Check password
Expand Down
24 changes: 21 additions & 3 deletions htdocs/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,6 @@
#==============================================================================
require_once("../vendor/autoload.php");
require_once("../lib/functions.inc.php");
if ($use_captcha) {
require_once("../lib/captcha.inc.php");
}

#==============================================================================
# VARIABLES
Expand Down Expand Up @@ -122,6 +119,11 @@
isset($ldap_krb5ccname) ? $ldap_krb5ccname : null
);

#==============================================================================
# Captcha Config
#==============================================================================
require_once(__DIR__ . "/../lib/captcha.inc.php");

#==============================================================================
# Other default values
#==============================================================================
Expand Down Expand Up @@ -209,6 +211,19 @@
auditlog($audit_log_file, $userdn, $login, $action, $result);
}

#==============================================================================
# Generate captcha
#==============================================================================
$captcha_html = '';
$captcha_js = '';
$captcha_css = '';
if(isset($use_captcha) && $use_captcha == true)
{
$captcha_html = $captchaInstance->generate_html_captcha($messages);
$captcha_js = $captchaInstance->generate_js_captcha();
$captcha_css = $captchaInstance->generate_css_captcha();
}

#==============================================================================
# Smarty
#==============================================================================
Expand Down Expand Up @@ -266,6 +281,9 @@
$smarty->assign('mail_address_use_ldap', $mail_address_use_ldap);
$smarty->assign('sms_use_ldap', $sms_use_ldap);
$smarty->assign('default_action', $default_action);
$smarty->assign('captcha_html', $captcha_html);
$smarty->assign('captcha_js', $captcha_js);
$smarty->assign('captcha_css', $captcha_css);
//$smarty->assign('',);

if (isset($source)) { $smarty->assign('source', $source); }
Expand Down
17 changes: 17 additions & 0 deletions htdocs/newcaptcha.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

require_once("../conf/config.inc.php");
require_once("../vendor/autoload.php");

# load captcha
require_once(__DIR__ . "/../lib/captcha.inc.php");

$captcha_challenge = $captchaInstance->generate_captcha_challenge();
$result = array(
'challenge' => "$captcha_challenge",
);

header('Content-type: application/json');
echo json_encode($result);

?>
2 changes: 1 addition & 1 deletion htdocs/resetbyquestions.php
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@
#==============================================================================
# Check captcha
#==============================================================================
if ( ( $result === "" ) and $use_captcha) { $result = global_captcha_check();}
if ( ( $result === "" ) and $use_captcha) { $result = $captchaInstance->verify_captcha_challenge();}

# Should we pre-populate the question?
# This should ensure that $login is valid and everything else is empty.
Expand Down
2 changes: 1 addition & 1 deletion htdocs/sendsms.php
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@
# Check captcha
#==============================================================================
if ( $result === "" and $use_captcha) {
$result = global_captcha_check();
$result = $captchaInstance->verify_captcha_challenge();
}

#==============================================================================
Expand Down
2 changes: 1 addition & 1 deletion htdocs/sendtoken.php
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
# Check captcha
#==============================================================================
if ( ( $result === "" ) and $use_captcha) {
$result = global_captcha_check();
$result = $captchaInstance->verify_captcha_challenge();
}

#==============================================================================
Expand Down
2 changes: 1 addition & 1 deletion htdocs/setattributes.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
#==============================================================================
# Check captcha
#==============================================================================
if ( ( $result === "" ) and $use_captcha) { $result = global_captcha_check();}
if ( ( $result === "" ) and $use_captcha) { $result = $captchaInstance->verify_captcha_challenge();}

#==============================================================================
# Check password
Expand Down
2 changes: 1 addition & 1 deletion htdocs/setquestions.php
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@
#==============================================================================
# Check captcha
#==============================================================================
if ( ( $result === "" ) and $use_captcha) { $result = global_captcha_check();}
if ( ( $result === "" ) and $use_captcha) { $result = $captchaInstance->verify_captcha_challenge();}

#==============================================================================
# Check password
Expand Down
Loading

0 comments on commit fc2a753

Please sign in to comment.