Overview
Comment:Sécurité: obligation de confirmer activation OTP, ajout clef PGP pour chiffrement mails sortants, déplacement infos sécurité dans une page à part
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | dev
Files: files | file ages | folders
SHA1: 71857e568008f5cd6de9ea0130640465b5845b0a
User & Date: bohwaz on 2017-02-17 04:31:38
Other Links: branch diff | manifest | tags
Context
2017-02-21
03:55
Sécurité: vérification de la clé PGP avant d'enregistrer check-in: 36fa993386 user: bohwaz tags: dev
2017-02-17
04:31
Sécurité: obligation de confirmer activation OTP, ajout clef PGP pour chiffrement mails sortants, déplacement infos sécurité dans une page à part check-in: 71857e5680 user: bohwaz tags: dev
02:08
Ajout signaux pour plugin, fix [743d7e1483fc23c85bd66aa44bd5673479a88913] check-in: a1acf12dcf user: bohwaz tags: dev
Changes

Modified src/include/data/0.8.0.sql from [737893df79] to [70e25e73cc].

1
2



-- Ajouter champ pour OTP
ALTER TABLE membres ADD COLUMN secret_otp TEXT NULL;





>
>
>
1
2
3
4
5
-- Ajouter champ pour OTP
ALTER TABLE membres ADD COLUMN secret_otp TEXT NULL;

-- Ajouter champ clé PGP
ALTER TABLE membres ADD COLUMN clef_pgp TEXT NULL;

Modified src/include/lib/Garradin/Membres.php from [6c7caf4cad] to [4506047015].

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
120
121
122
123
124
125
126
127

128
129
130
131
132
133
134

135



136

137
138
139
140

141
142
143
144
145
146
147
        if (empty($_SESSION['logged_user']))
        {
            return false;
        }

        $membre = $_SESSION['logged_user'];

        if (!\KD2\Security_OTP::TOTP($membre['secret_otp'], $code))
        {
            // Vérifier encore, mais avec le temps NTP
            // au cas où l'horloge du serveur n'est pas à l'heure
            $time = \KD2\Security_OTP::getTimeFromNTP('fr.pool.ntp.org');

            if (!\KD2\Security_OTP::TOTP($membre['secret_otp'], $code, $time))
            {
                return false;
            }
        }

        $_SESSION['otp_required'] = false;

        return true;
    }

    public function setOTP()
    {
        $membre = $this->getLoggedUser();

        $secret = \KD2\Security_OTP::getRandomSecret();

        DB::getInstance()->simpleExec('UPDATE membres SET secret_otp = ? WHERE id = ?;', $secret, $membre['id']);

        $membre['secret_otp'] = $secret;

        $this->updateSessionData($membre);


        return $secret;
    }

    public function disableOTP()
    {
        $membre = $this->getLoggedUser();





        DB::getInstance()->simpleExec('UPDATE membres SET secret_otp = NULL WHERE id = ?;', $membre['id']);


        $membre['secret_otp'] = null;

        $this->updateSessionData($membre);


        return true;
    }

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







|

<
<
<
<
<
<
|
|
|






|

<
|
|
|
|
|
<
|
<
>

|


|

<
>
|
>
>
>
|
>
|
|
|
<
>







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
120
121
122
123
124
125

126
127
128
129
130
131
132
133
134
135

136
137
138
139
140
141
142
143
        if (empty($_SESSION['logged_user']))
        {
            return false;
        }

        $membre = $_SESSION['logged_user'];

        if (!$this->checkOTP($membre['secret_otp'], $code))
        {






            return false;
        }


        $_SESSION['otp_required'] = false;

        return true;
    }

    public function getNewOTPSecret()
    {

        $out = [];
        $out['secret'] = \KD2\Security_OTP::getRandomSecret();
        $out['secret_display'] = implode(' ', str_split($out['secret'], 4));
        $out['url'] = \KD2\Security_OTP::getOTPAuthURL(Config::getInstance()->get('nom_asso'), $out['secret']);
    

        $qrcode = new \KD2\QRCode($out['url']);

        $out['qrcode'] = 'data:image/svg+xml;base64,' . base64_encode($qrcode->toSVG());

        return $out;
    }

    public function checkOTP($secret, $code)
    {

        if (!\KD2\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 = \KD2\Security_OTP::getTimeFromNTP('fr.pool.ntp.org');

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

        }

        return true;
    }

    public function recoverPasswordCheck($id)
    {
        $db = DB::getInstance();
568
569
570
571
572
573
574
























































575
576
577
578
579
580
581
        if (empty($data))
        {
            return true;
        }

        return $db->simpleUpdate('membres', $data, 'id = '.(int)$id);
    }

























































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

        return $db->simpleQuerySingle('SELECT *,







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







564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
        if (empty($data))
        {
            return true;
        }

        return $db->simpleUpdate('membres', $data, 'id = '.(int)$id);
    }

    public function checkPassword($password)
    {
        $user = $this->getLoggedUser();

        if (!$user)
        {
            return false;
        }

        return $this->_checkPassword($password, $user['passe']);
    }

    public function editSecurity(Array $data = [])
    {
        $user = $this->getLoggedUser();

        if (!$user)
        {
            throw new \LogicException('Utilisateur non connecté.');
        }

        $allowed_fields = ['passe', 'clef_pgp', 'secret_otp'];

        foreach ($data as $key=>$value)
        {
            if (!in_array($key, $allowed_fields))
            {
                throw new \RuntimeException(sprintf('Le champ %s n\'est pas autorisé dans cette méthode.', $key));
            }
        }

        if (isset($data['passe']) && trim($data['passe']) !== '')
        {
            if (strlen($data['passe']) < 5)
            {
                throw new UserException('Le mot de passe doit faire au moins 5 caractères.');
            }

            $data['passe'] = $this->_hashPassword($data['passe']);
        }
        else
        {
            unset($data['passe']);
        }

        if (isset($data['clef_pgp']))
        {
            $data['clef_pgp'] = trim($data['clef_pgp']);
        }

        $db->simpleUpdate('membres', $data, 'id = '.(int)$user['id']);
        $this->updateSessionData();

        return true;
    }

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

        return $db->simpleQuerySingle('SELECT *,

Modified src/include/lib/Garradin/Membres/Champs.php from [d222f084dc] to [2915d1081b].

386
387
388
389
390
391
392
393

394
395
396
397
398
399
400
401
402
403
404
405
406

407
408
409
410
411
412
413

    	// Champs à créer
    	$create = [
    		'id INTEGER PRIMARY KEY, -- Numéro attribué automatiquement',
    		'id_categorie INTEGER NOT NULL, -- Numéro de catégorie',
            'date_connexion TEXT NULL, -- Date de dernière connexion',
            'date_inscription TEXT NOT NULL DEFAULT CURRENT_DATE, -- Date d\'inscription',
            'secret_otp TEXT NULL, -- Code secret pour TOTP'

    	];

        $create_keys = [
            'FOREIGN KEY (id_categorie) REFERENCES membres_categories (id)'
        ];

    	// Champs à recopier
    	$copy = [
    		'id',
    		'id_categorie',
            'date_connexion',
            'date_inscription',
            'secret_otp',

    	];

        $anciens_champs = $config->get('champs_membres');
    	$anciens_champs = is_null($anciens_champs) ? $this->champs : $anciens_champs->getAll();

    	foreach ($this->champs as $key=>$cfg)
    	{







|
>













>







386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415

    	// Champs à créer
    	$create = [
    		'id INTEGER PRIMARY KEY, -- Numéro attribué automatiquement',
    		'id_categorie INTEGER NOT NULL, -- Numéro de catégorie',
            'date_connexion TEXT NULL, -- Date de dernière connexion',
            'date_inscription TEXT NOT NULL DEFAULT CURRENT_DATE, -- Date d\'inscription',
            'secret_otp TEXT NULL, -- Code secret pour TOTP',
            'clef_pgp TEXT NULL, -- Clé publique PGP'
    	];

        $create_keys = [
            'FOREIGN KEY (id_categorie) REFERENCES membres_categories (id)'
        ];

    	// Champs à recopier
    	$copy = [
    		'id',
    		'id_categorie',
            'date_connexion',
            'date_inscription',
            'secret_otp',
            'clef_pgp',
    	];

        $anciens_champs = $config->get('champs_membres');
    	$anciens_champs = is_null($anciens_champs) ? $this->champs : $anciens_champs->getAll();

    	foreach ($this->champs as $key=>$cfg)
    	{

Modified src/templates/admin/mes_infos.tpl from [105fa33fc8] to [701b28771a].

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
{include file="admin/_head.tpl" title="Mes informations personnelles" current="mes_infos" js=1}

{if $error}
    <p class="error">
        {$error}
    </p>
{elseif $otp_status == 'off'}
    <p class="confirm">
        L'authentification à double facteur a été désactivée.
    </p>
{elseif $otp_status}
    <div class="alert">
        <img class="qrcode" src="{$otp_qrcode}" alt="" />
        <p class="confirm">L'authentification à double facteur a été activée.</p>
        <p class="help">
            Votre clé secrète est&nbsp;:<br />
            <code>{$otp_status}</code><br />
            Recopiez-la ou scannez le QR code pour configurer votre application.

        </p>
    </div>
{/if}

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


    <fieldset>
        <legend>Informations personnelles</legend>
        <dl>
            {foreach from=$champs item="champ" key="nom"}
            {if empty($champ.private) && $nom != 'passe'}
                {html_champ_membre config=$champ name=$nom data=$membre user_mode=true}
            {/if}
            {/foreach}
        </dl>
    </fieldset>

    <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="password_suggest" value="{$passphrase}" />
                </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
            l'authentification à double facteur. Cela nécessite d'installer une application comme <a href="https://freeotp.github.io/">FreeOTP</a>
            sur votre téléphone.</p>
        <dl>
            <dt>Authentification à double facteur (TOTP)</dt>
        {if $user.secret_otp}
            <dd><label><input type="radio" name="otp" value="" checked="checked" /> <strong>Activée</strong></label></dd>
            <dd><label><input type="radio" name="otp" value="generate" /> Régénérer une nouvelle clé secrète</label></dd>
            <dd><label><input type="radio" name="otp" value="disable" /> Désactiver l'authentification à double facteur</label></dd>
        {else}
            <dd><em>Désactivée</em></dd>
            <dd><label><input type="checkbox" name="otp" value="generate" /> Activer</label></dd>
        {/if}
        </dl>
    </fieldset>

    <p class="submit">
        {csrf_field key="edit_me"}
        <input type="submit" name="save" value="Enregistrer &rarr;" />
    </p>

</form>


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

{include file="admin/_foot.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
{include file="admin/_head.tpl" title="Mes informations personnelles" current="mes_infos" js=1}

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



{/if}

<ul class="actions">


    <li class="current"><a href="{$admin_url}mes_infos.php">Mes informations personnelles</a></li>



    <li><a href="{$admin_url}mes_infos_securite.php">Mot de passe et options de sécurité</a></li>
</ul>



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


    <fieldset>
        <legend>Informations personnelles</legend>
        <dl>
            {foreach from=$champs item="champ" key="nom"}
            {if empty($champ.private) && $nom != 'passe'}
                {html_champ_membre config=$champ name=$nom data=$membre user_mode=true}
            {/if}
            {/foreach}
        </dl>
    </fieldset>

    <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}

















            <p><a href="{$admin_url}mes_infos_securite.php">Modifier mon mot de passe ou autres informations de sécurité.</a></p>














        {/if}

    </fieldset>

    <p class="submit">
        {csrf_field key="edit_me"}
        <input type="submit" name="save" value="Enregistrer &rarr;" />
    </p>

</form>










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

Added src/templates/admin/mes_infos_securite.tpl version [b578d4e8a9].



























































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
{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>
            <img class="qrcode" src="{$otp.qrcode}" alt="" />
            <dl>
                <dt>Ma clé secrète est&nbsp;:</dt>
                <dd><code>{$otp.secret_display}</code></dd>
                <dd class="help">Recopiez la clé secrète ou scannez le QR code pour configurer votre application TOTP (par exemple <a href="https://freeotp.github.io/">FreeOTP</a>), puis utilisez celle-ci pour générer un code d'accès et confirmer l'activation.</dd>
                <dt><label for="f_code">Code TOTP</label></dt>
                <dd class="help">Entrez ici le code donné par l'application d'authentification double facteur.</dd>
                <dd><input type="text" name="code" id="f_code" value="{form_field name=code}" /></dd>
            </dl>
        </fieldset>
    {/if}

    <fieldset>
        <legend>Confirmer les changements</legend>
        <dl>
            <dt><label for="f_passe_confirm">Mot de passe actuel</label></dt>
            <dd class="help">Entrez votre mot de passe actuel pour confirmer les changements demandés.</dd>
            <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="password_suggest" value="{$passphrase}" />
                    </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
                l'authentification à double facteur. Cela nécessite d'installer une application comme <a href="https://freeotp.github.io/">FreeOTP</a>
                sur votre téléphone.</p>
            <dl>
                <dt>Authentification à double facteur (TOTP)</dt>
            {if $membre.secret_otp}
                <dd><label><input type="radio" name="otp" value="" checked="checked" /> <strong>Activée</strong></label></dd>
                <dd><label><input type="radio" name="otp" value="generate" /> Régénérer une nouvelle clé secrète</label></dd>
                <dd><label><input type="radio" name="otp" value="disable" /> Désactiver l'authentification à double facteur</label></dd>
            {else}
                <dd><em>Désactivée</em></dd>
                <dd><label><input type="checkbox" name="otp" value="generate" /> Activer</label></dd>
            {/if}
            </dl>
        </fieldset>

        {if $pgp_disponible}
        <fieldset>
            <legend>Protéger mes mails personnels par chiffrement PGP/GnuPG</legend>
            <dl>
                <dt><label for="f_clef_pgp">Ma clé publique PGP</label></dt>
                <dd class="help">En inscrivant ici votre clé publique, tous les emails personnels (non collectifs) qui vous
                    sont envoyés seront chiffrés (cryptés) avec cette clé&nbsp;: messages envoyés par les membres, rappels de cotisation,
                    procédure de récupération de mot de passe, etc.</dd>
                <dd><textarea name="clef_pgp" id="f_clef_pgp" cols="90" rows="5">{form_field name="clef_pgp" data=$user}</textarea></dd>
                {if $clef_pgp_fingerprint}<dd class="help">L'empreinte de la clé est&nbsp;: {$clef_pgp_fingerprint}</dd>{/if}
            </dl>
            <p class="alert">
                Attention&nbsp;: en inscrivant ici votre clé PGP, les emails de récupération de mot de passe perdu vous seront envoyés chiffrés
                et ne pourront être lus sans utiliser le mot de passe protégeant votre clé privée correspondante.
            </p>
        </fieldset>
        {/if}

        <p class="submit">
            {csrf_field key="edit_me_security"}
            <input type="submit" name="save" value="Enregistrer &rarr;" />
        </p>

    </form>

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

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

Modified src/www/admin/mes_infos.php from [15a9109208] to [16b21a0d0d].

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

if (!empty($_POST['save']))
{
    if (!Utils::CSRF_check('edit_me'))
    {
        $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.';
    }
    else
    {
        try {
            $data = [];

            foreach ($config->get('champs_membres')->getAll() as $key=>$c)
            {
                if (!empty($c['editable']))
                {
                    $data[$key] = Utils::post($key);
                }
            }

            $membres->edit($membre['id'], $data, false);
            $membres->updateSessionData();

            if (Utils::post('otp') == 'generate')
            {
                $secret = $membres->setOTP();
                Utils::redirect('/admin/mes_infos.php?otp=' . rawurlencode($secret));
            }
            elseif (Utils::post('otp') == 'disable')
            {
                $secret = $membres->disableOTP();
                Utils::redirect('/admin/mes_infos.php?otp=off');
            }
            else
            {
                Utils::redirect('/admin/');
            }
        }
        catch (UserException $e)
        {
            $error = $e->getMessage();
        }
    }
}

$tpl->assign('error', $error);
$tpl->assign('otp_status', Utils::get('otp'));

if (Utils::get('otp') && Utils::get('otp') != 'off')
{
    $url = \KD2\Security_OTP::getOTPAuthURL($config->get('nom_asso'), Utils::get('otp'));
    $qrcode = new \KD2\QRCode($url);
    $qrcode = 'data:image/svg+xml;base64,' . base64_encode($qrcode->toSVG());
    $tpl->assign('otp_qrcode', $qrcode);

    $tpl->assign('otp_status', implode(' ', str_split(Utils::get('otp'), 4)));
}

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

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

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







<
<
<
<
















<
<
<
<
<
<
<
<
<
<
<
<
|
<









<

<
<
<
<
<
<
<
<
<
<
<





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

if (!empty($_POST['save']))
{
    if (!Utils::CSRF_check('edit_me'))
    {
        $error = 'Une erreur est survenue, merci de renvoyer le formulaire.';
    }




    else
    {
        try {
            $data = [];

            foreach ($config->get('champs_membres')->getAll() as $key=>$c)
            {
                if (!empty($c['editable']))
                {
                    $data[$key] = Utils::post($key);
                }
            }

            $membres->edit($membre['id'], $data, false);
            $membres->updateSessionData();













            Utils::redirect('/admin/');

        }
        catch (UserException $e)
        {
            $error = $e->getMessage();
        }
    }
}

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













$tpl->assign('champs', $config->get('champs_membres')->getAll());

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

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

Added src/www/admin/mes_infos_securite.php version [89a6bd6a26].















































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
<?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.';
    }
    else
    {
        $confirm = true;
    }
}

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

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

$tpl->assign('pgp_disponible', \KD2\Security::canUseEncryption());
$tpl->assign('clef_pgp_fingerprint', !empty($membre['clef_pgp']) ? \KD2\Security::getEncryptionKeyFingerprint($membre['clef_pgp']) : null);

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

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

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