Completed
Push — master ( ecdd68...e69772 )
by Arnold
02:25
created

Auth::hashPassword()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 8
rs 9.4285
c 0
b 0
f 0
nc 2
cc 3
eloc 4
nop 1
1
<?php
2
3
namespace Jasny;
4
5
use Jasny\Auth\User;
6
7
/**
8
 * Authentication and access control
9
 * 
10
 * <code>
11
 * class Auth extends Jasny\Auth
12
 * {
13
 *     use Jasny\Auth\ByLevel;
14
 *     use Jasny\Auth\Sessions;
15
 * 
16
 *     protected $levels = [
17
 *       'user' => 1,
18
 *       'admin' => 10
19
 *     ];
20
 * 
21
 *     public function fetchUserById($id)
22
 *     {
23
 *         ...
24
 *     }
25
 * 
26
 *     public function fetchUserByUsername($username)
27
 *     {
28
 *         ...
29
 *     }
30
 * }
31
 * </code>
32
 */
33
abstract class Auth
34
{
35
    /**
36
     * Current authenticated user
37
     * @var User|false
38
     */
39
    protected $user;
40
    
41
    
42
    /**
43
     * Persist the current user id across requests
44
     */
45
    abstract protected function persistCurrentUser();
46
    
47
    /**
48
     * Get current authenticated user id
49
     * 
50
     * @return mixed
51
     */
52
    abstract protected function getCurrentUserId();
53
    
54
    /**
55
     * Fetch a user by ID
56
     * 
57
     * @param int|string $id
58
     * @return User|null
59
     */
60
    abstract public function fetchUserById($id);
61
62
    /**
63
     * Fetch a user by username
64
     * 
65
     * @param string $username
66
     * @return User|null
67
     */
68
    abstract public function fetchUserByUsername($username);
69
    
70
    
71
    /**
72
     * Get current authenticated user
73
     * 
74
     * @return User|null
75
     */
76
    public function user()
77
    {
78
        if (!isset($this->user)) {
79
            $uid = $this->getCurrentUserId();
80
            $this->user = $uid ? ($this->fetchUserById($uid) ?: false) : false;
81
        }
82
        
83
        return $this->user ?: null;
84
    }
85
    
86
    /**
87
     * Set the current user
88
     * 
89
     * @param User $user
90
     * @return boolean
91
     */
92
    public function setUser(User $user)
93
    {
94
        if ($user->onLogin() === false) {
95
            return null;
96
        }
97
        
98
        $this->user = $user;
99
        $this->persistCurrentUser();
100
        
101
        return $this->user;
102
    }
103
    
104
    
105
    /**
106
     * Hash a password
107
     * 
108
     * @param string $password
109
     * @return string
110
     */
111
    public function hashPassword($password)
112
    {
113
        if (!is_string($password) || $password === '') {
114
            throw new \InvalidArgumentException("Password should be a (non-empty) string");
115
        }
116
        
117
        return password_hash($password, PASSWORD_BCRYPT);
118
    }
119
    
120
    /**
121
     * Fetch user and verify credentials.
122
     * 
123
     * @param User|null $user
124
     * @param string    $password
125
     * @return boolean
126
     */
127
    public function verifyCredentials($user, $password)
128
    {
129
        return isset($user) && password_verify($password, $user->getHashedPassword());
130
    }
131
    
132
    /**
133
     * Login with username and password
134
     * 
135
     * @param string $username
136
     * @param string $password
137
     * @return User|null
138
     */
139
    public function login($username, $password)
140
    {
141
        $user = $this->fetchUserByUsername($username);
142
143
        if (!$this->verifyCredentials($user, $password)) {
144
            return null;
145
        }
146
        
147
        return static::setUser($user);
0 ignored issues
show
Bug introduced by
It seems like $user defined by $this->fetchUserByUsername($username) on line 141 can be null; however, Jasny\Auth::setUser() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
148
    }
149
    
150
    /**
151
     * Logout
152
     */
153
    public function logout()
154
    {
155
        $user = $this->user();
156
        
157
        if (!$user) {
158
            return;
159
        }
160
        
161
        $user->onLogout();
162
        
163
        $this->user = false;
164
        $this->persistCurrentUser();
165
    }
166
}
167