• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

daycry / auth / 25518434194

07 May 2026 07:49PM UTC coverage: 58.608% (-6.4%) from 64.989%
25518434194

push

github

web-flow
Merge pull request #47 from daycry/development

Implement security enhancements and new account features

277 of 1030 new or added lines in 55 files covered. (26.89%)

11 existing lines in 6 files now uncovered.

3544 of 6047 relevant lines covered (58.61%)

47.97 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

85.37
/src/Models/LoginModel.php
1
<?php
2

3
declare(strict_types=1);
4

5
/**
6
 * This file is part of Daycry Auth.
7
 *
8
 * (c) Daycry <daycry9@proton.me>
9
 *
10
 * For the full copyright and license information, please view
11
 * the LICENSE file that was distributed with this source code.
12
 */
13

14
namespace Daycry\Auth\Models;
15

16
use CodeIgniter\I18n\Time;
17
use Daycry\Auth\Authentication\Authenticators\Session;
18
use Daycry\Auth\Entities\Login;
19
use Daycry\Auth\Entities\User;
20
use Faker\Generator;
21

22
class LoginModel extends BaseModel
23
{
24
    protected $primaryKey     = 'id';
25
    protected $returnType     = Login::class;
26
    protected $useSoftDeletes = false;
27
    protected $allowedFields  = [
28
        'ip_address',
29
        'user_agent',
30
        'id_type',
31
        'identifier',
32
        'user_id',
33
        'date',
34
        'success',
35
    ];
36
    protected $useTimestamps   = false;
37
    protected $validationRules = [
38
        'ip_address' => 'required',
39
        'id_type'    => 'required',
40
        'identifier' => 'permit_empty|string',
41
        'user_agent' => 'permit_empty|string',
42
        'user_id'    => 'permit_empty|integer',
43
        'date'       => 'required|valid_date',
44
    ];
45
    protected $validationMessages = [];
46
    protected $skipValidation     = false;
47

48
    protected function initialize(): void
161✔
49
    {
50
        parent::initialize();
161✔
51

52
        $this->table = $this->tables['logins'];
161✔
53
    }
54

55
    /**
56
     * Records login attempt.
57
     *
58
     * @param string          $idType Identifier type. See const ID_YPE_* in Authenticator.
59
     *                                auth_logins: 'email_password'|'username'|'magic-link'
60
     *                                auth_token_logins: 'access-token'
61
     * @param int|string|null $userId
62
     */
63
    public function recordLoginAttempt(
33✔
64
        string $idType,
65
        ?string $identifier,
66
        bool $success,
67
        ?string $ipAddress = null,
68
        ?string $userAgent = null,
69
        ?int $userId = null,
70
    ): void {
71
        $this->disableDBDebug();
33✔
72

73
        if ($this->db->getPlatform() === 'OCI8' && $identifier === '') {
33✔
74
            $identifier = ' ';
×
75
        }
76

77
        $return = $this->insert([
33✔
78
            'ip_address' => $ipAddress,
33✔
79
            'user_agent' => $userAgent,
33✔
80
            'id_type'    => $idType,
33✔
81
            'identifier' => $identifier,
33✔
82
            'user_id'    => $userId,
33✔
83
            'date'       => Time::now()->format('Y-m-d H:i:s'),
33✔
84
            'success'    => (int) $success,
33✔
85
        ]);
33✔
86

87
        $this->checkQueryReturn($return);
33✔
88
    }
89

90
    /**
91
     * Returns the previous login information for the user,
92
     * useful to display to the user the last time the account
93
     * was accessed.
94
     */
95
    public function previousLogin(User $user): ?Login
1✔
96
    {
97
        return $this->where('success', 1)
1✔
98
            ->where('user_id', $user->id)
1✔
99
            ->orderBy('id', 'desc')
1✔
100
            ->limit(1, 1)->first();
1✔
101
    }
102

103
    /**
104
     * Returns the last login information for the user
105
     */
106
    public function lastLogin(User $user): ?Login
1✔
107
    {
108
        return $this->where('success', 1)
1✔
109
            ->where('user_id', $user->id)
1✔
110
            ->orderBy('id', 'desc')
1✔
111
            ->first();
1✔
112
    }
113

114
    /**
115
     * Returns the recent login history for a user (newest first).
116
     *
117
     * Used by user-facing activity feeds — includes both successful and
118
     * failed attempts so the user can spot suspicious activity targeting
119
     * their account.
120
     *
121
     * @return list<Login>
122
     */
NEW
123
    public function recentForUser(User $user, int $limit = 25): array
×
124
    {
NEW
125
        return $this->where('user_id', $user->id)
×
NEW
126
            ->orderBy('id', 'desc')
×
NEW
127
            ->limit($limit)
×
NEW
128
            ->find();
×
129
    }
130

131
    /**
132
     * Generate a fake login for testing
133
     */
134
    public function fake(Generator &$faker): Login
2✔
135
    {
136
        return new Login([
2✔
137
            'ip_address' => $faker->ipv4(),
2✔
138
            'id_type'    => Session::ID_TYPE_EMAIL_PASSWORD,
2✔
139
            'identifier' => $faker->email(),
2✔
140
            'user_id'    => null,
2✔
141
            'date'       => Time::parse('-1 day')->format('Y-m-d H:i:s'),
2✔
142
            'success'    => true,
2✔
143
        ]);
2✔
144
    }
145
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc