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

Confirmation   A

Complexity

Total Complexity 9

Size/Duplication

Total Lines 81
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 1

Importance

Changes 0
Metric Value
wmc 9
lcom 1
cbo 1
dl 0
loc 81
rs 10
c 0
b 0
f 0

5 Methods

Rating   Name   Duplication   Size   Complexity  
fetchUserById() 0 1 ?
getConfirmationSecret() 0 1 ?
A generateConfirmHash() 0 6 1
A getConfirmationToken() 0 16 2
B fetchUserForConfirmation() 0 18 6
1
<?php
2
3
namespace Jasny\Auth;
4
5
use Hashids\Hashids;
6
7
/**
8
 * Generate and verify a hash to confirm a new user.
9
 * 
10
 * Uses the hashids library
11
 * @link http://hashids.org/php/
12
 * 
13
 * <code>
14
 * class Auth extends Jasny\Auth
15
 * {
16
 *   use Jasny\Auth\Confirmation;
17
 * 
18
 *   public function getConfirmationSecret()
19
 *   {
20
 *     return "f)lk3sd^92qlj$%f8321*(&lk";
21
 *   }
22
 * 
23
 *   ...
24
 * }
25
 * </code>
26
 */
27
trait Confirmation
28
{
29
    /**
30
     * Fetch a user by ID
31
     * 
32
     * @param int|string $id
33
     * @return User|null
34
     */
35
    abstract public function fetchUserById($id);
36
    
37
    /**
38
     * Get secret for the confirmation hash
39
     * 
40
     * @return string
41
     */
42
    abstract protected function getConfirmationSecret();
43
44
    
45
    /**
46
     * Generate a confirm hash based on a user id
47
     * 
48
     * @param string $id
49
     * @return string
50
     */
51
    protected function generateConfirmHash($id)
52
    {
53
        $confirmHash = hash('sha256', $id . $this->getConfirmationSecret());
54
        
55
        return sprintf('%012s', substr(base_convert($confirmHash, 16, 36), -12));
56
    }
57
    
58
    /**
59
     * Generate a confirmation token
60
     * 
61
     * @param User   $user
62
     * @param string $subject  What needs to be confirmed?
63
     * @return string
64
     */
65
    public function getConfirmationToken(User $user, $subject)
66
    {
67
        if (!class_exists(Hashids::class)) {
68
            throw new \Exception("Unable to generate a confirmation hash: Hashids library is not installed");
69
        }
70
        
71
        $id = $user->getId();
72
        $confirm = $this->generateConfirmHash($id);
73
        
74
        $salt = hash('sha256', $this->getConfirmationSecret());
75
        $hashids = new Hashids($salt);
76
        
77
        $decId = hexdec($id); // Will work if id is hexidecimal or decimal
78
        
79
        return $hashids->encode($subject, $decId, $confirm);
80
    }
81
    
82
    /**
83
     * Get user by confirmation hash
84
     * 
85
     * @param string $token    Confirmation token
86
     * @param string $subject  What needs to be confirmed?
87
     * @return User|null
88
     */
89
    public function fetchUserForConfirmation($token, $subject)
90
    {
91
        if (!class_exists(Hashids::class)) {
92
            throw new \Exception("Unable to generate a confirmation hash: Hashids library is not installed");
93
        }
94
        
95
        $hashids = new Hashids($this->getConfirmationSalt());
0 ignored issues
show
Bug introduced by
It seems like getConfirmationSalt() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
96
        
97
        list($decId, $tokenSubject, $confirm) = (array)$hashids->decode($token) + [null, null, null];
98
        
99
        $id = isset($decId) ? hexdec($decId) : null; // Inverse action of getConfirmationToken
100
        
101
        if (!isset($id) || $tokenSubject !== $subject || $confirm !== $this->generateConfirmHash($id)) {
102
            return null;
103
        }
104
105
        return $this->fetchUserById($id);
106
    }
107
}
108