Passed
Branch master (bf41b2)
by Stefan
04:13
created

SilverbulletInvitation::__construct()   C

Complexity

Conditions 8
Paths 17

Size

Total Lines 62
Code Lines 47

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 62
rs 6.943
c 0
b 0
f 0
cc 8
eloc 47
nc 17
nop 1

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/*
4
 * ******************************************************************************
5
 * Copyright 2011-2017 DANTE Ltd. and GÉANT on behalf of the GN3, GN3+, GN4-1 
6
 * and GN4-2 consortia
7
 *
8
 * License: see the web/copyright.php file in the file structure
9
 * ******************************************************************************
10
 */
11
12
/**
13
 * This file contains the SilverbulletInvitation class.
14
 *
15
 * @author Stefan Winter <[email protected]>
16
 * @author Tomasz Wolniewicz <[email protected]>
17
 *
18
 * @package Developer
19
 *
20
 */
21
22
namespace core;
23
24
use \Exception;
25
26
class SilverbulletInvitation extends common\Entity {
27
28
    /**
29
     * row ID in the database pertaining to this invitation. 0 on invalid invitations.
30
     * 
31
     * @var int
32
     */
33
    public $identifier;
34
35
    /**
36
     * The profile this invitation belongs to. 0 on invalid invitations.
37
     * 
38
     * @var int
39
     */
40
    public $profile;
41
42
    /**
43
     * The user this invitation was created for (integer DB ID). 0 on invalid invitations.
44
     * 
45
     * @var int
46
     */
47
    public $userId;
48
49
    /**
50
     *
51
     * @var string
52
     */
53
    public $invitationTokenString;
54
55
    /**
56
     * 
57
     * @var int
58
     */
59
    public $invitationTokenStatus;
60
61
    /**
62
     * Expiry timestamp of invitation token. 2000-01-01 00:00:00 on invalid invitations.
63
     * 
64
     * @var string
65
     */
66
    public $expiry;
67
68
    /**
69
     * How many devices were allowed to be activated in total? 0 on invalid invitations.
70
     * 
71
     * @var int
72
     */
73
    public $activationsTotal;
74
75
    /**
76
     * How many devices have not yet been activated? 0 on invalid invitations.
77
     *
78
     * @var int
79
     */
80
    public $activationsRemaining;
81
82
    /**
83
     * 
84
     * @var array
85
     */
86
    public $associatedCertificates;
87
88
    /**
89
     * handle to the database
90
     * 
91
     * @var DBConnection
92
     */
93
    private $databaseHandle;
94
95
    const SB_TOKENSTATUS_VALID = 0;
96
    const SB_TOKENSTATUS_PARTIALLY_REDEEMED = 1;
97
    const SB_TOKENSTATUS_REDEEMED = 2;
98
    const SB_TOKENSTATUS_EXPIRED = 3;
99
    const SB_TOKENSTATUS_INVALID = 4;
100
101
    public function __construct($invitationId) {
102
        parent::__construct();
103
        $this->invitationTokenString = $invitationId;
104
        $this->databaseHandle = DBConnection::handle("INST");
105
        /*
106
         * Finds invitation by its token attribute and loads all certificates generated using the token.
107
         * Certificate details will always be empty, since code still needs to be adapted to return multiple certificates information.
108
         */
109
        $invColumnNames = "`id`, `profile_id`, `silverbullet_user_id`, `token`, `quantity`, `expiry`";
110
        $invitationsResult = $this->databaseHandle->exec("SELECT $invColumnNames FROM `silverbullet_invitation` WHERE `token`=? ORDER BY `expiry` DESC", "s", $this->invitationTokenString);
111
        $this->associatedCertificates = [];
112
        if ($invitationsResult->num_rows == 0) {
113
            $this->loggerInstance->debug(2, "Token $this->invitationTokenString not found in database or database query error!\n");
114
            $this->invitationTokenStatus = SilverbulletInvitation::SB_TOKENSTATUS_INVALID;
115
            $this->identifier = 0;
116
            $this->profile = 0;
117
            $this->userId = 0;
118
            $this->expiry = "2000-01-01 00:00:00";
119
            $this->activationsTotal = 0;
120
            $this->activationsRemaining = 0;
121
            return;
122
        }
123
        // if not returned, we found the token in the DB
124
        // -> instantiate the class
125
        // SELECT -> resource, no boolean
126
        $invitationRow = mysqli_fetch_object(/** @scrutinizer ignore-type */ $invitationsResult);
127
        $this->identifier = $invitationRow->id;
128
        $this->profile = $invitationRow->profile_id;
129
        $this->userId = $invitationRow->silverbullet_user_id;
130
        $this->expiry = $invitationRow->expiry;
131
        $this->activationsTotal = $invitationRow->quantity;
132
        $certificatesResult = $this->databaseHandle->exec("SELECT `serial_number` FROM `silverbullet_certificate` WHERE `silverbullet_invitation_id` = ? ORDER BY `revocation_status`, `expiry` DESC", "i", $this->identifier);
133
        $certificatesNumber = ($certificatesResult ? $certificatesResult->num_rows : 0);
134
        $this->loggerInstance->debug(5, "At token validation level, " . $certificatesNumber . " certificates exist.\n");
135
        // SELECT -> resource, no boolean
136
        while ($runner = mysqli_fetch_object(/** @scrutinizer ignore-type */ $certificatesResult)) {
137
            $this->associatedCertificates[] = new \core\SilverbulletCertificate($runner->serial_number);
138
        }
139
        $this->activationsRemaining = (int) $this->activationsTotal - (int) $certificatesNumber;
140
        switch ($certificatesNumber) {
141
            case 0:
142
                // find out if it has expired
143
                $now = new \DateTime();
144
                $expiryObject = new \DateTime($this->expiry);
145
                $delta = $now->diff($expiryObject);
146
                if ($delta->invert == 1) {
147
                    $this->invitationTokenStatus = SilverbulletInvitation::SB_TOKENSTATUS_EXPIRED;
148
                    $this->activationsRemaining = 0;
149
                    break;
150
                }
151
                $this->invitationTokenStatus = SilverbulletInvitation::SB_TOKENSTATUS_VALID;
152
                break;
153
            case $invitationRow->quantity:
154
                $this->invitationTokenStatus = SilverbulletInvitation::SB_TOKENSTATUS_REDEEMED;
155
                break;
156
            default:
157
                assert($certificatesNumber > 0); // no negatives allowed
158
                assert($certificatesNumber < $invitationRow->quantity || $invitationRow->quantity == 0); // not more than max quantity allowed (unless quantity is zero)
159
                $this->invitationTokenStatus = SilverbulletInvitation::SB_TOKENSTATUS_PARTIALLY_REDEEMED;
160
        }
161
162
        $this->loggerInstance->debug(5, "Done creating invitation token state from DB.\n");
163
    }
164
165
    public function link() {
166
        if (isset($_SERVER['HTTPS'])) {
167
            $link = 'https://';
168
        } else {
169
            $link = 'http://';
170
        }
171
        $link .= $_SERVER['SERVER_NAME'];
172
        $relPath = dirname(dirname($_SERVER['SCRIPT_NAME']));
173
        if (substr($relPath, -1) == '/') {
174
            $relPath = substr($relPath, 0, -1);
175
            if ($relPath === FALSE) {
1 ignored issue
show
introduced by
The condition $relPath === FALSE can never be true.
Loading history...
176
                throw new Exception("Uh. Something went seriously wrong with URL path mangling.");
177
            }
178
        }
179
        $link = $link . $relPath;
180
181
        if (preg_match('/admin$/', $link)) {
182
            $link = substr($link, 0, -6);
183
            if ($link === FALSE) {
1 ignored issue
show
introduced by
The condition $link === FALSE can never be true.
Loading history...
184
                throw new Exception("Impossible: the string ends with '/admin' but it's not possible to cut six characters from the end?!");
185
            }
186
        }
187
188
        return $link . '/accountstatus/accountstatus.php?token=' . $this->invitationTokenString;
189
    }
190
191
    /**
192
     * returns the subject to use in an invitation mail
193
     * @return string
194
     */
195
    public function invitationMailSubject() {
196
        return sprintf(_("Your %s access is ready"), CONFIG_CONFASSISTANT['CONSORTIUM']['display_name']);
197
    }
198
199
    /**
200
     * returns the body to use in an invitation mail
201
     * @return string
202
     */
203
    public function invitationMailBody() {
204
        $text = _("Hello!");
205
        $text .= "\n\n";
206
        $text .= sprintf(_("A new %s access credential has been created for you by your network administrator."), CONFIG_CONFASSISTANT['CONSORTIUM']['display_name']);
207
        $text .= " ";
208
        $text .= sprintf(_("Please follow the following link with the device you want to enable for %s to get a custom %s installation program just for you. You can click on the link, copy and paste it into a browser or scan the attached QR code."), CONFIG_CONFASSISTANT['CONSORTIUM']['display_name'], CONFIG_CONFASSISTANT['CONSORTIUM']['display_name']);
209
        $text .= "\n\n" . $this->link() . "\n\n"; // gets replaced with the token value by getBody()
210
        $text .= sprintf(_("Please keep this email or bookmark this link for future use. After picking up your %s installation program, you can use the same link to get status information about your %s account."), CONFIG_CONFASSISTANT['CONSORTIUM']['display_name'], CONFIG_CONFASSISTANT['CONSORTIUM']['display_name']);
211
        $text .= "\n\n";
212
        $text .= _("Regards,");
213
        $text .= "\n\n";
214
        $text .= sprintf("%s", CONFIG['APPEARANCE']['productname_long']);
215
216
        return $text;
217
    }
218
219
    /**
220
     * generates a new hex string to be used as an activation token
221
     * 
222
     * @return string
223
     */
224
    private static function generateInvitation() {
225
        return hash("sha512", base_convert(rand(0, (int) 10e16), 10, 36));
226
    }
227
228
    /**
229
     * creates a new invitation in the database
230
     * @param int $profileId
231
     * @param int $userId
232
     * @param int $activationCount
233
     */
234
    public static function createInvitation($profileId, $userId, $activationCount) {
235
        $handle = DBConnection::handle("INST");
236
        $query = "INSERT INTO silverbullet_invitation (profile_id, silverbullet_user_id, token, quantity, expiry) VALUES (?, ?, ?, ?, DATE_ADD(NOW(), INTERVAL 7 DAY))";
237
        $newToken = SilverbulletInvitation::generateInvitation();
238
        $handle->exec($query, "iisi", $profileId, $userId, $newToken, $activationCount);
239
        return new SilverbulletInvitation($newToken);
240
    }
241
242
    /**
243
     * revokes an invitation
244
     */
245
    public function revokeInvitation() {
246
        $query = "UPDATE silverbullet_invitation SET expiry = NOW() WHERE id = ? AND profile_id = ?";
247
        $this->databaseHandle->exec($query, "ii", $this->invitationTokenString, $this->identifier);
248
    }
249
250
}
251