Overview
Comment:Modernisation page mes_infos_securite
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | dev
Files: files | file ages | folders
SHA1: c2c740f5546ef0724afa7a53a4c68aa82fc278ef
User & Date: bohwaz on 2017-05-16 06:11:34
Other Links: branch diff | manifest | tags
Context
2017-05-17
06:35
Modernisation page "mes cotisations" check-in: e8680f70ea user: bohwaz tags: dev
2017-05-16
06:11
Modernisation page mes_infos_securite check-in: c2c740f554 user: bohwaz tags: dev
06:10
Utilisation de Form au lieu de Security pour faire du CSRF check-in: a80706f2b7 user: bohwaz tags: dev
Changes

Modified src/include/lib/Garradin/Membres/Session.php from [ba5886b165] to [6d9c786107].

215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237

















238
239
240
241
242
243
244
		$user = $_SESSION['otp'];

		if (empty($user->secret) || empty($user->id))
		{
			return false;
		}

		if (!Security_OTP::TOTP($user->secret, $code))
		{
			// Vérifier encore, mais avec le temps NTP
			// au cas où l'horloge du serveur n'est pas à l'heure
			$time = Security_OTP::getTimeFromNTP(NTP_SERVER);

			if (!Security_OTP::TOTP($user->secret, $code, $time))
			{
				return false;
			}
		}

		$session = new Session($user->id);
		$session->updateLoginDate();
		return $session;
	}


















	static public function recoverPasswordCheck($id)
	{
		$db = DB::getInstance();
		$config = Config::getInstance();

		$champ_id = $config->get('champ_identifiant');







|
<
<
<
<
<
<



<





>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







215
216
217
218
219
220
221
222






223
224
225

226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
		$user = $_SESSION['otp'];

		if (empty($user->secret) || empty($user->id))
		{
			return false;
		}

		if (!self::checkOTP($user->secret, $code))






			{
				return false;
			}


		$session = new Session($user->id);
		$session->updateLoginDate();
		return $session;
	}

	static public function checkOTP($secret, $code)
	{
		if (!Security_OTP::TOTP($secret, $code))
		{
			// Vérifier encore, mais avec le temps NTP
			// au cas où l'horloge du serveur n'est pas à l'heure
			$time = Security_OTP::getTimeFromNTP(NTP_SERVER);

			if (!Security_OTP::TOTP($secret, $code, $time))
			{
				return false;
			}
		}

		return true;
	}

	static public function recoverPasswordCheck($id)
	{
		$db = DB::getInstance();
		$config = Config::getInstance();

		$champ_id = $config->get('champ_identifiant');

Modified src/include/lib/Garradin/Template.php from [d7cfbaa69e] to [ed95de2191].

42
43
44
45
46
47
48


49
50
51





52
53
54
55
56
57
58
        {
            return '';
        }

        $out = [];

        foreach ($errors as $error)


        {
            $out[] = $this->getFormErrorMessage($error['rule'], $error['name']);
        }






        return '<div class="error"><ul><li>' . implode('</li><li>', $out) . '</li></ul></div>';
    }

    protected function getFormErrorMessage($rule, $element)
    {
        if ($element == '_id')







>
>



>
>
>
>
>







42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
        {
            return '';
        }

        $out = [];

        foreach ($errors as $error)
        {
            if (is_array($error))
        {
            $out[] = $this->getFormErrorMessage($error['rule'], $error['name']);
        }
            else
            {
                $out[] = $error;
            }
        }

        return '<div class="error"><ul><li>' . implode('</li><li>', $out) . '</li></ul></div>';
    }

    protected function getFormErrorMessage($rule, $element)
    {
        if ($element == '_id')

Modified src/templates/admin/mes_infos_securite.tpl from [31aa919cb6] to [0d4da55bf9].

1
2
3
4
5
6
7


8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
{include file="admin/_head.tpl" title="Mes informations de connexion et sécurité" current="mes_infos" js=1}

<ul class="actions">
    <li><a href="{$admin_url}mes_infos.php">Mes informations personnelles</a></li>
    <li class="current"><a href="{$admin_url}mes_infos_securite.php">Mot de passe et options de sécurité</a></li>
</ul>



{if $confirm}
    <form method="post" action="{$self_url}">

    {if $error}
        <p class="error">
            {$error}
        </p>
    {/if}

    {if !empty($otp)}
        <p class="alert">
            Confirmez l'activation de l'authentification à double facteur TOTP en l'utilisant une première fois.
        </p>

        <fieldset>
            <legend>Confirmer l'activation de l'authentification à double facteur (2FA)</legend>







>
>



<
<
<
<
<
<







1
2
3
4
5
6
7
8
9
10
11
12






13
14
15
16
17
18
19
{include file="admin/_head.tpl" title="Mes informations de connexion et sécurité" current="mes_infos" js=1}

<ul class="actions">
    <li><a href="{$admin_url}mes_infos.php">Mes informations personnelles</a></li>
    <li class="current"><a href="{$admin_url}mes_infos_securite.php">Mot de passe et options de sécurité</a></li>
</ul>

{form_errors}

{if $confirm}
    <form method="post" action="{$self_url}">







    {if !empty($otp)}
        <p class="alert">
            Confirmez l'activation de l'authentification à double facteur TOTP en l'utilisant une première fois.
        </p>

        <fieldset>
            <legend>Confirmer l'activation de l'authentification à double facteur (2FA)</legend>
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
            <dd><input type="password" name="passe_confirm" /></dd>
        </dl>
    </fieldset>

    <p class="submit">
        {csrf_field key="edit_me_security"}
        <input type="hidden" name="passe" value="{form_field name="passe"}" />
        <input type="hidden" name="repasse" value="{form_field name="repasse"}" />
        <input type="hidden" name="clef_pgp" value="{form_field name="clef_pgp"}" />
        <input type="hidden" name="otp_secret" value="{$otp.secret}" />
        <input type="hidden" name="otp" value="generate" />
        <input type="submit" name="confirm" value="Confirmer &rarr;" />
    </p>

    </form>
{else}

    {if $error}
        <p class="error">
            {$error}
        </p>
    {/if}

    <form method="post" action="{$self_url}">

        <fieldset>
            <legend>Changer mon mot de passe</legend>
            {if $user.droits.membres < Garradin\Membres::DROIT_ADMIN && (!empty($champs.passe.private) || empty($champs.passe.editable))}
                <p class="help">Vous devez contacter un administrateur pour changer votre mot de passe.</p>
            {else}
                <dl>
                    <dd>Vous avez déjà un mot de passe, ne remplissez les champs suivants que si vous souhaitez en changer.</dd>
                    <dt><label for="f_passe">Nouveau mot de passe</label></dt>
                    <dd class="help">
                        Astuce : un mot de passe de quatre mots choisis au hasard dans le dictionnaire est plus sûr 
                        et plus simple à retenir qu'un mot de passe composé de 10 lettres et chiffres.
                    </dd>
                    <dd class="help">
                        Pas d'idée&nbsp;? Voici une suggestion choisie au hasard :
                        <input type="text" readonly="readonly" title="Cliquer pour utiliser cette suggestion comme mot de passe" id="pw_suggest" value="{$passphrase}" autocomplete="off" />
                    </dd>
                    <dd><input type="password" name="passe" id="f_passe" value="{form_field name=passe}" pattern=".{ldelim}5,{rdelim}" /></dd>
                    <dt><label for="f_repasse">Encore le mot de passe</label> (vérification)</dt>
                    <dd><input type="password" name="repasse" id="f_repasse" value="{form_field name=repasse}" pattern=".{ldelim}5,{rdelim}" /></dd>
                </dl>
            {/if}
        </fieldset>

        <fieldset>
            <legend>Authentification à double facteur (2FA)</legend>
            <p class="help">Pour renforcer la sécurité de votre connexion en cas de vol de votre mot de passe, vous pouvez activer







|









<
<
<
<
<
<




















|







37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53






54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
            <dd><input type="password" name="passe_confirm" /></dd>
        </dl>
    </fieldset>

    <p class="submit">
        {csrf_field key="edit_me_security"}
        <input type="hidden" name="passe" value="{form_field name="passe"}" />
        <input type="hidden" name="passe_confirmed" value="{form_field name="passe_confirmed"}" />
        <input type="hidden" name="clef_pgp" value="{form_field name="clef_pgp"}" />
        <input type="hidden" name="otp_secret" value="{$otp.secret}" />
        <input type="hidden" name="otp" value="generate" />
        <input type="submit" name="confirm" value="Confirmer &rarr;" />
    </p>

    </form>
{else}







    <form method="post" action="{$self_url}">

        <fieldset>
            <legend>Changer mon mot de passe</legend>
            {if $user.droits.membres < Garradin\Membres::DROIT_ADMIN && (!empty($champs.passe.private) || empty($champs.passe.editable))}
                <p class="help">Vous devez contacter un administrateur pour changer votre mot de passe.</p>
            {else}
                <dl>
                    <dd>Vous avez déjà un mot de passe, ne remplissez les champs suivants que si vous souhaitez en changer.</dd>
                    <dt><label for="f_passe">Nouveau mot de passe</label></dt>
                    <dd class="help">
                        Astuce : un mot de passe de quatre mots choisis au hasard dans le dictionnaire est plus sûr 
                        et plus simple à retenir qu'un mot de passe composé de 10 lettres et chiffres.
                    </dd>
                    <dd class="help">
                        Pas d'idée&nbsp;? Voici une suggestion choisie au hasard :
                        <input type="text" readonly="readonly" title="Cliquer pour utiliser cette suggestion comme mot de passe" id="pw_suggest" value="{$passphrase}" autocomplete="off" />
                    </dd>
                    <dd><input type="password" name="passe" id="f_passe" value="{form_field name=passe}" pattern=".{ldelim}5,{rdelim}" /></dd>
                    <dt><label for="f_repasse">Encore le mot de passe</label> (vérification)</dt>
                    <dd><input type="password" name="passe_confirmed" id="f_passe_confirmed" value="{form_field name=passe_confirmed}" pattern=".{ldelim}5,{rdelim}" /></dd>
                </dl>
            {/if}
        </fieldset>

        <fieldset>
            <legend>Authentification à double facteur (2FA)</legend>
            <p class="help">Pour renforcer la sécurité de votre connexion en cas de vol de votre mot de passe, vous pouvez activer
128
129
130
131
132
133
134
135
136
137
138
139
140
141
        </p>

    </form>

    <script type="text/javascript">
    {literal}
    g.script('scripts/password.js').onload = function () {
        initPasswordField('pw_suggest', 'f_passe', 'f_repasse');
    };
    {/literal}
    </script>
{/if}

{include file="admin/_foot.tpl"}







|






118
119
120
121
122
123
124
125
126
127
128
129
130
131
        </p>

    </form>

    <script type="text/javascript">
    {literal}
    g.script('scripts/password.js').onload = function () {
        initPasswordField('pw_suggest', 'f_passe', 'f_passe_confirmed');
    };
    {/literal}
    </script>
{/if}

{include file="admin/_foot.tpl"}

Modified src/www/admin/mes_infos_securite.php from [a0932f5a86] to [57647c5fb5].

1
2
3
4
5
6
7
8
9
10

11
12
13
14
15
16
17
18
19


20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48


49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84

85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
<?php
namespace Garradin;

require_once __DIR__ . '/_inc.php';

$membre = $membres->getLoggedUser();

if (!$membre)
{
    throw new UserException("Ce membre n'existe pas.");

}

$error = false;
$confirm = false;

if (!empty($_POST['confirm']))
{
    if (!Utils::CSRF_check('edit_me_security'))
    {


        $error = 'Une erreur est survenue, merci de renvoyer le formulaire.';
    }
    elseif (trim(Utils::post('passe_confirm')) === '')
    {
        $error = 'Merci de bien vouloir renseigner votre mot de passe courant pour confirmer les changements.';
    }
    elseif ($membres->checkPassword(Utils::post('passe_confirm')))
    {
        $error = 'Le mot de passe fourni ne correspond pas au mot de passe actuel. Merci de bien vouloir renseigner votre mot de passe courant pour confirmer les changements.';
    }
    elseif (Utils::post('passe') != Utils::post('repasse'))
    {
        $error = 'La vérification ne correspond pas au mot de passe.';
    }
    elseif (Utils::post('otp_secret') && !$membres->checkOTP(Utils::post('otp_secret'), Utils::post('code')))
    {
        $error = 'Le code TOTP entré n\'est pas valide.';
    }
    else
    {
        try {
            $data = [
                'clef_pgp' => Utils::post('clef_pgp'),
            ];

            if (Utils::post('passe') && !empty($config->get('champs_membres')->get('passe')['editable']))
            {
                $data['passe'] = Utils::post('passe');
            }



            if (Utils::post('otp_secret'))
            {
                $data['secret_otp'] = Utils::post('otp_secret');
            }
            elseif (Utils::post('otp') == 'disable')
            {
                $data['secret_otp'] = null;
            }

            $membres->editSecurity($data);
            Utils::redirect('/admin/');
        }
        catch (UserException $e)
        {
            $error = $e->getMessage();
        }
    }

    $confirm = true;
}
elseif (Utils::post('save'))
{
    if (!Utils::CSRF_check('edit_me_security'))
    {
        $error = 'Une erreur est survenue, merci de renvoyer le formulaire.';
    }
    elseif (Utils::post('passe') != Utils::post('repasse'))
    {
        $error = 'La vérification ne correspond pas au mot de passe.';
    }
    elseif (Utils::post('clef_pgp') && !$membres->getPGPFingerprint(Utils::post('clef_pgp')))
    {
        $error = 'Clé PGP invalide : impossible de récupérer l\'empreinte de la clé.';
    }
    else

    {
        $confirm = true;
    }
}

$tpl->assign('error', $error);
$tpl->assign('confirm', $confirm);

if (Utils::post('otp') == 'generate')
{
    $otp = $membres->getNewOTPSecret();
    $tpl->assign('otp', $otp);
}
else
{
    $tpl->assign('otp', false);
}

$tpl->assign('pgp_disponible', \KD2\Security::canUseEncryption());

$fingerprint = '';

if ($membre['clef_pgp'])
{
    $fingerprint = $membres->getPGPFingerprint($membre['clef_pgp'], true);
}

$tpl->assign('clef_pgp_fingerprint', $fingerprint);

$tpl->assign('passphrase', Utils::suggestPassword());
$tpl->assign('champs', $config->get('champs_membres')->getAll());

$tpl->assign('membre', $membre);

$tpl->display('admin/mes_infos_securite.tpl');

<

<
|
<

|
|
<
>
|
<
|


|

|
<
>
>
|
|
|

|

|

<
<
<
<
|

<
|
|
<
<



|


|

|

>
>
|
<
<
|

|




|




|





|

|
<
<
<
|
<
|
|
|

|

|
>





|


|

|











|

|







|


1

2

3

4
5
6

7
8

9
10
11
12
13
14

15
16
17
18
19
20
21
22
23
24




25
26

27
28


29
30
31
32
33
34
35
36
37
38
39
40
41


42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62



63

64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
<?php



namespace Garradin;


use Garradin\Membres\Session;


require_once __DIR__ . '/_inc.php';


$errors = [];
$confirm = false;

if (f('confirm'))
{
    fc('edit_me_security', [

        'passe'       => 'confirmed',
        'passe_check' => 'required',
    ], $errors);

    if (f('passe_check') && !$session->checkPassword(f('passe_check')))
    {
        $errors[] = 'Le mot de passe fourni ne correspond pas au mot de passe actuel. Merci de bien vouloir renseigner votre mot de passe courant pour confirmer les changements.';
    }
    elseif (f('otp_secret') && !Session::checkOTP(f('otp_secret'), f('code')))
    {




        $errors[] = 'Le code TOTP entré n\'est pas valide.';
    }


    if (count($errors) === 0)


    {
        try {
            $data = [
                'clef_pgp' => f('clef_pgp'),
            ];

            if (f('passe') && !empty($config->get('champs_membres')->get('passe')->editable))
            {
                $data['passe'] = f('passe');
            }

            if (f('otp_secret'))
            {


                $data['secret_otp'] = f('otp_secret');
            }
            elseif (f('otp') == 'disable')
            {
                $data['secret_otp'] = null;
            }

            $session->editSecurity($data);
            Utils::redirect('/admin/');
        }
        catch (UserException $e)
        {
            $errors[] = $e->getMessage();
        }
    }

    $confirm = true;
}
elseif (f('save'))
{
    fc('edit_me_security', [



        'passe'       => 'confirmed',

    ], $errors);

    if (f('clef_pgp') && !$session->getPGPFingerprint(f('clef_pgp')))
    {
        $errors[] = 'Clé PGP invalide : impossible de récupérer l\'empreinte de la clé.';
    }
    
    if (count($errors) === 0)
    {
        $confirm = true;
    }
}

$tpl->assign('form_errors', $errors);
$tpl->assign('confirm', $confirm);

if (f('otp') == 'generate')
{
    $otp = $session->getNewOTPSecret();
    $tpl->assign('otp', $otp);
}
else
{
    $tpl->assign('otp', false);
}

$tpl->assign('pgp_disponible', \KD2\Security::canUseEncryption());

$fingerprint = '';

if ($user->clef_pgp)
{
    $fingerprint = $session->getPGPFingerprint($user->clef_pgp, true);
}

$tpl->assign('clef_pgp_fingerprint', $fingerprint);

$tpl->assign('passphrase', Utils::suggestPassword());
$tpl->assign('champs', $config->get('champs_membres')->getAll());

$tpl->assign('membre', $user);

$tpl->display('admin/mes_infos_securite.tpl');