Completed
Branch master (078ac8)
by Pierre-Henry
35:09
created

_protected/framework/Mvc/Model/Security.class.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * @title            Security Model Class
4
 *
5
 * @author           Pierre-Henry Soria <[email protected]>
6
 * @copyright        (c) 2012-2017, Pierre-Henry Soria. All Rights Reserved.
7
 * @license          GNU General Public License; See PH7.LICENSE.txt and PH7.COPYRIGHT.txt in the root directory.
8
 * @package          PH7 / Framework / Mvc / Model
9
 * @version          1.3
10
 */
11
12
namespace PH7\Framework\Mvc\Model;
13
defined('PH7') or exit('Restricted access');
14
15
use
0 ignored issues
show
There must be a single space after the USE keyword
Loading history...
16
PH7\Framework\Mvc\Model\Engine\Db,
17
PH7\Framework\Ip\Ip,
18
PH7\Framework\Mvc\Model\Engine\Util\Various;
19
20
class Security
21
{
22
23
    private $_sIp, $_sCurrentTime;
24
25
    public function __construct()
26
    {
27
        $this->_sIp = Ip::get();
28
        $this->_sCurrentTime = (new \PH7\Framework\Date\CDateTime)->get()->dateTime('Y-m-d H:i:s');
29
    }
30
31
    /**
32
     * Block user IP
33
     *
34
     * @param string $sIp IP address.
35
     * @param integer $iExpir Expiration in seconds. Default 86400
36
     * @return boolean Returns TRUE if no IP has been found (and the new IP has been added to the block list), otherwise FALSE.
37
     */
38
    public function blockIp($sIp, $iExpir = 86400)
39
    {
40
        $iExpir = time() + (int) $iExpir;
41
        $rStmt = Db::getInstance()->prepare('SELECT ip FROM' . Db::prefix('BlockIp') . 'WHERE ip = :ip LIMIT 1');
42
        $rStmt->bindValue(':ip', $sIp, \PDO::PARAM_STR);
43
        $rStmt->execute();
44
45
        if ($rStmt->rowCount() == 0) // Not Found IP
46
        {
47
            $rStmt = Db::getInstance()->prepare('INSERT INTO'.Db::prefix('BlockIp'). 'VALUES (:ip, :expiration)');
48
            $rStmt->bindValue(':ip', $sIp, \PDO::PARAM_STR);
49
            $rStmt->bindValue(':expiration', $iExpir, \PDO::PARAM_INT);
50
            $rStmt->execute();
51
            return true;
52
        }
53
        return false;
54
    }
55
56
    /**
57
     * Add Login Log.
58
     *
59
     * @param string $sEmail
60
     * @param string $sUsername
61
     * @param string $sPassword
62
     * @param integer $sStatus
63
     * @param string $sTable Default 'Members'
64
     * @return void
65
     */
66
    public function addLoginLog($sEmail, $sUsername, $sPassword, $sStatus, $sTable = 'Members')
67
    {
68
        Various::checkModelTable($sTable);
69
70
        $rStmt = Db::getInstance()->prepare('INSERT INTO' . Db::prefix($sTable.'LogLogin') . '(email, username, password, status, ip)
71
        VALUES (:email, :username, :password, :status, :ip)');
72
        $rStmt->bindValue(':email', $sEmail, \PDO::PARAM_STR);
73
        $rStmt->bindValue(':username', $sUsername, \PDO::PARAM_STR);
74
        $rStmt->bindValue(':password', $sPassword, \PDO::PARAM_STR);
75
        $rStmt->bindValue(':status', $sStatus, \PDO::PARAM_STR);
76
        $rStmt->bindValue(':ip', $this->_sIp, \PDO::PARAM_STR);
77
        $rStmt->execute();
78
        Db::free($rStmt);
79
    }
80
81
    /**
82
     * Blocking access to the login page after exceeded login attempts.
83
     *
84
     * @param integer $iMaxAttempts
85
     * @param integer $iAttemptTime
86
     * @param string $sEmail Email address of member.
87
     * @param object \PH7\Framework\Layout\Tpl\Engine\PH7Tpl\PH7Tpl $oView
88
     * @param string $sTable Default 'Members'
89
     * @return boolean Returns TRUE if attempts are allowed, FALSE otherwise.
90
     */
91
    public function checkLoginAttempt($iMaxAttempts, $iAttemptTime, $sEmail, \PH7\Framework\Layout\Tpl\Engine\PH7Tpl\PH7Tpl $oView, $sTable = 'Members')
92
    {
93
        Various::checkModelTable($sTable);
94
95
        $rStmt = Db::getInstance()->prepare('SELECT * FROM' . Db::prefix($sTable.'AttemptsLogin') . 'WHERE ip = :ip LIMIT 1');
96
        $rStmt->bindValue(':ip', $this->_sIp, \PDO::PARAM_STR);
97
        $rStmt->execute();
98
99
        if ($rStmt->rowCount() == 1)
100
        {
101
            $oRow = $rStmt->fetch(\PDO::FETCH_OBJ);
102
103
            if ($oRow->attempts >= $iMaxAttempts)
104
            {
105
                $sLockoutTime = (new \DateTime($oRow->lastLogin))->add(\DateInterval::createFromDateString("$iAttemptTime minutes"))->format('Y-m-d H:i:s');
106
107
                if ($sLockoutTime > $this->_sCurrentTime)
108
                {
109
                    /**
110
                     * Send email to prevent that someone tries to hack their member account.
111
                     * We test that the number of attempts equals the number of maximim tantatives to avoid duplication of sending emails.
112
                     */
113
                    if ($oRow->attempts == $iMaxAttempts)
114
                        (new \PH7\Security)->sendAlertLoginAttemptsExceeded($iMaxAttempts, $iAttemptTime, $this->_sIp, $sEmail, $oView, $sTable);
115
                }
116
                else
117
                {
118
                    // Clear Login Attempts
119
                    $this->clearLoginAttempts($sTable);
120
                    return true; // Authorized
121
                }
122
                return false; // Banned
123
            }
124
        }
125
        return true; // Authorized
126
    }
127
128
    /**
129
     * Add Loging Attempt.
130
     *
131
     * @param string $sTable Default 'Members'
132
     * @return void
133
     */
134
    public function addLoginAttempt($sTable = 'Members')
135
    {
136
        Various::checkModelTable($sTable);
137
138
        $rStmt = Db::getInstance()->prepare('SELECT * FROM' . Db::prefix($sTable.'AttemptsLogin') . 'WHERE ip = :ip LIMIT 1');
139
        $rStmt->bindValue(':ip', $this->_sIp, \PDO::PARAM_STR);
140
        $rStmt->execute();
141
142
        if ($rStmt->rowCount() == 1)
143
        {
144
            $oRow = $rStmt->fetch(\PDO::FETCH_OBJ);
145
            $iAttempts = $oRow->attempts+1;
146
            $rStmt = Db::getInstance()->prepare('UPDATE' . Db::prefix($sTable.'AttemptsLogin') . 'SET attempts = :attempts, lastLogin = :currentTime WHERE ip = :ip');
147
            $rStmt->bindValue(':ip', $this->_sIp, \PDO::PARAM_STR);
148
            $rStmt->bindValue(':attempts', $iAttempts, \PDO::PARAM_INT);
149
            $rStmt->bindValue(':currentTime', $this->_sCurrentTime, \PDO::PARAM_STR);
150
            $rStmt->execute();
151
        }
152
        else
153
        {
154
            $rStmt = Db::getInstance()->prepare('INSERT INTO' . Db::prefix($sTable.'AttemptsLogin') . '(ip, attempts, lastLogin) VALUES (:ip, 1, :lastLogin)');
155
            $rStmt->bindValue(':ip', $this->_sIp, \PDO::PARAM_STR);
156
            $rStmt->bindValue(':lastLogin', $this->_sCurrentTime, \PDO::PARAM_STR);
157
            $rStmt->execute();
158
        }
159
160
        Db::free($rStmt);
161
    }
162
163
    /**
164
     * Clear Login Attempts.
165
     *
166
     * @param string $sTable Default 'Members'
167
     * @return void
168
     */
169
    public function clearLoginAttempts($sTable = 'Members')
170
    {
171
        Various::checkModelTable($sTable);
172
173
        $rStmt = Db::getInstance()->prepare('DELETE FROM' . Db::prefix($sTable.'AttemptsLogin') . 'WHERE ip = :ip');
174
        $rStmt->bindValue(':ip', $this->_sIp, \PDO::PARAM_STR);
175
        $rStmt->execute();
176
        Db::free($rStmt);
177
    }
178
179
}
180