Issues (195)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

Alpha/Model/Person.php (2 issues)

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
namespace Alpha\Model;
4
5
use Alpha\Model\Type\SmallText;
6
use Alpha\Model\Type\Enum;
7
use Alpha\Model\Type\Relation;
8
use Alpha\Util\Helper\Validator;
9
use Alpha\Util\Logging\Logger;
10
use Alpha\Util\Config\ConfigProvider;
11
use Alpha\Util\Service\ServiceFactory;
12
use Alpha\Exception\RecordNotFoundException;
13
14
/**
15
 * The main person/user class for the framework.
16
 *
17
 * @since 1.0
18
 *
19
 * @author John Collins <[email protected]>
20
 * @license http://www.opensource.org/licenses/bsd-license.php The BSD License
21
 * @copyright Copyright (c) 2018, John Collins (founder of Alpha Framework).
22
 * All rights reserved.
23
 *
24
 * <pre>
25
 * Redistribution and use in source and binary forms, with or
26
 * without modification, are permitted provided that the
27
 * following conditions are met:
28
 *
29
 * * Redistributions of source code must retain the above
30
 *   copyright notice, this list of conditions and the
31
 *   following disclaimer.
32
 * * Redistributions in binary form must reproduce the above
33
 *   copyright notice, this list of conditions and the
34
 *   following disclaimer in the documentation and/or other
35
 *   materials provided with the distribution.
36
 * * Neither the name of the Alpha Framework nor the names
37
 *   of its contributors may be used to endorse or promote
38
 *   products derived from this software without specific
39
 *   prior written permission.
40
 *
41
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
42
 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
43
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
44
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
45
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
46
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
47
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
48
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
49
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
50
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
51
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
52
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
53
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
54
 * </pre>
55
 */
56
class Person extends ActiveRecord
57
{
58
    /**
59
     * The forum display name of the person.
60
     *
61
     * @var \Alpha\Model\Type\SmallText
62
     *
63
     * @since 1.0
64
     */
65
    protected $username;
66
67
    /**
68
     * The email address for the person.
69
     *
70
     * @var \Alpha\Model\Type\SmallText
71
     *
72
     * @since 1.0
73
     */
74
    protected $email;
75
76
    /**
77
     * The password for the person.
78
     *
79
     * @var \Alpha\Model\Type\SmallText
80
     *
81
     * @since 1.0
82
     */
83
    protected $password;
84
85
    /**
86
     * A Relation containing all of the rights groups that this person belongs to.
87
     *
88
     * @var \Alpha\Model\Type\Relation
89
     *
90
     * @since 1.0
91
     */
92
    protected $rights;
93
94
    /**
95
     * A Relation containing all of the actions carried out by this person.
96
     *
97
     * @var \Alpha\Model\Type\Relation
98
     *
99
     * @since 1.2.2
100
     */
101
    protected $actions;
102
103
    /**
104
     * An array of data display labels for the class properties.
105
     *
106
     * @var array
107
     *
108
     * @since 1.0
109
     */
110
    protected $dataLabels = array('ID' => 'Member ID#',
111
                                    'username' => 'Username',
112
                                    'email' => 'E-mail Address',
113
                                    'password' => 'Password',
114
                                    'state' => 'Account state',
115
                                    'URL' => 'Your site address',
116
                                    'rights' => 'Rights Group Membership',
117
                                    'actions' => 'Actions',);
118
119
    /**
120
     * The name of the database table for the class.
121
     *
122
     * @var string
123
     *
124
     * @since 1.0
125
     */
126
    const TABLE_NAME = 'Person';
127
128
    /**
129
     * The state of the person (account status).
130
     *
131
     * @var Aplha\Model\Type\Enum
132
     *
133
     * @since 1.0
134
     */
135
    protected $state;
136
137
    /**
138
     * The website URL of the person.
139
     *
140
     * @var \Alpha\Model\Type\SmallText
141
     *
142
     * @since 1.0
143
     */
144
    protected $URL;
145
146
    /**
147
     * Trace logger.
148
     *
149
     * @var \Alpha\Util\Logging\Logger
150
     *
151
     * @since 1.0
152
     */
153
    private static $logger = null;
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
154
155
    /**
156
     * Constructor for the class that populates all of the complex types with default values.
157
     *
158
     * @since 1.0
159
     */
160
    public function __construct()
161
    {
162
        self::$logger = new Logger('Person');
163
        self::$logger->debug('>>__construct()');
164
165
        // ensure to call the parent constructor
166
        parent::__construct();
167
        $this->username = new SmallText();
168
        $this->username->setRule(Validator::REQUIRED_USERNAME);
169
        $this->username->setSize(70);
170
        $this->username->setHelper('Please provide a name for display on the website (only letters, numbers, and .-_ characters are allowed!).');
171
        $this->email = new SmallText();
172
        $this->email->setRule(Validator::REQUIRED_EMAIL);
173
        $this->email->setSize(70);
174
        $this->email->setHelper('Please provide a valid e-mail address as your username.');
175
        $this->password = new SmallText();
176
        $this->password->setSize(70);
177
        $this->password->setHelper('Please provide a password for logging in.');
178
        $this->password->isPassword(true);
179
        $this->state = new Enum(array(
180
                                    'Active',
181
                                    'Disabled',));
182
        $this->state->setValue('Active');
183
        $this->URL = new SmallText();
184
        $this->URL->setRule(Validator::OPTIONAL_HTTP_URL);
185
        $this->URL->setHelper('URLs must be in the format http://some_domain/ or left blank!');
186
        // add unique keys to username and email (which is effectively the username in Alpha)
187
        $this->markUnique('username');
188
        $this->markUnique('email');
189
190
        $this->rights = new Relation();
191
        $this->markTransient('rights');
192
193
        $this->actions = new Relation();
194
        $this->markTransient('actions');
195
196
        $this->setupRels();
197
198
        self::$logger->debug('<<__construct');
199
    }
200
201
    /**
202
     * Set up the transient attributes for the rights group after it has loaded.
203
     *
204
     * @since 1.0
205
     */
206
    protected function after_load_callback()
207
    {
208
        $this->setupRels();
209
    }
210
211
    /**
212
     * Set up the transient attributes for the site after it has loaded.
213
     *
214
     * @since 1.0
215
     */
216
    protected function after_loadByAttribute_callback()
217
    {
218
        $this->setupRels();
219
    }
220
221
    /**
222
     * Looks up the ID for the Standard rights group, then relates the new person
223
     * to that group if they are not in it already.  If that group does not exist it
224
     * will be recreated!
225
     *
226
     * @since 1.0
227
     */
228
    protected function after_save_callback()
229
    {
230
        if ($this->getVersionNumber()->getValue() == 1) {
231
            $standardGroup = new Rights();
232
233
            $this->setupRels();
234
235
            if (!$this->inGroup('Standard')) {
236
                try {
237
                    $standardGroup->loadByAttribute('name', 'Standard');
238
                } catch (RecordNotFoundException $e) {
239
                    $standardGroup->set('name', 'Standard');
240
                    $standardGroup->save();
241
                }
242
243
                $lookup = $this->rights->getLookup();
244
                $lookup->setValue(array($this->getID(), $standardGroup->getID()));
245
                $lookup->save();
246
            }
247
        }
248
    }
249
250
    /**
251
     * Encrypts password value if it is plaintext before saving.
252
     *
253
     * @since 2.0
254
     */
255
    protected function before_save_callback()
256
    {
257
        if (password_needs_rehash($this->get('password'), PASSWORD_DEFAULT, ['cost' => 12])) {
258
            $this->set('password', password_hash($this->get('password'), PASSWORD_DEFAULT, ['cost' => 12]));
259
        }
260
    }
261
262
    /**
263
     * Sets up the Relation definitions on this record.
264
     *
265
     * @since 1.0
266
     */
267
    protected function setupRels()
268
    {
269
        // set up MANY-TO-MANY relation person2rights
270
        if (isset($this->rights)) {
271
            $this->rights->setRelatedClass('Alpha\Model\Person', 'left');
272
            $this->rights->setRelatedClassDisplayField('email', 'left');
273
            $this->rights->setRelatedClass('Alpha\Model\Rights', 'right');
274
            $this->rights->setRelatedClassDisplayField('name', 'right');
275
            $this->rights->setRelationType('MANY-TO-MANY');
276
            $this->rights->setValue($this->getID());
277
        }
278
279
        if (isset($this->actions)) {
280
            $this->actions->setValue($this->ID);
281
            $this->actions->setRelatedClass('Alpha\Model\ActionLog');
282
            $this->actions->setRelatedClassField('created_by');
283
            $this->actions->setRelatedClassDisplayField('message');
284
            $this->actions->setRelationType('ONE-TO-MANY');
285
        }
286
    }
287
288
    /**
289
     * Setter for username.
290
     *
291
     * @param string $username
292
     *
293
     * @since 1.0
294
     */
295
    public function setUsername($username)
296
    {
297
        $this->username->setValue($username);
298
    }
299
300
    /**
301
     * Getter for username.
302
     *
303
     * @return \Alpha\Model\Type\SmallText
304
     *
305
     * @since 1.0
306
     */
307
    public function getUsername()
308
    {
309
        return $this->username;
310
    }
311
312
    /**
313
     * Checks to see if the person is in the rights group specified.
314
     *
315
     * @param string $groupName
316
     *
317
     * @return bool
318
     *
319
     * @since 1.0
320
     */
321
    public function inGroup($groupName)
322
    {
323
        if (self::$logger == null) {
324
            self::$logger = new Logger('Person');
325
        }
326
        self::$logger->debug('>>inGroup(groupName=['.$groupName.'])');
327
328
        $group = new Rights();
329
330
        try {
331
            $group->loadByAttribute('name', $groupName);
332
        } catch (RecordNotFoundException $e) {
333
            self::$logger->error('Unable to load the group named ['.$groupName.']');
334
            self::$logger->debug('<<inGroup [false]');
335
336
            return false;
337
        }
338
339
        $rel = $group->getMembers();
340
341
        try {
342
            // load all person2rights RelationLookup objects for this person
343
            $lookUps = $rel->getLookup()->loadAllByAttribute('leftID', $this->getID());
344
            foreach ($lookUps as $lookUp) {
345
                // the rightID (i.e. Rights ID) will be on the right side of the value array
346
                $ids = $lookUp->getValue();
347
                // if we have found a match, return true right away
348
                if ($ids[1] == $group->getID()) {
349
                    self::$logger->debug('<<inGroup [true]');
350
351
                    return true;
352
                }
353
            }
354
        } catch (RecordNotFoundException $e) {
355
            self::$logger->debug('<<inGroup [false]');
356
357
            return false;
358
        }
359
360
        self::$logger->debug('<<inGroup [false]');
361
362
        return false;
363
    }
364
365
    /**
366
     * Adds this person to the rights group specified.
367
     *
368
     * @param string $groupName
369
     *
370
     * @throws \Alpha\Exception\RecordNotFoundException
371
     *
372
     * @since 2.0
373
     */
374
    public function addToGroup($groupName)
375
    {
376
        if (self::$logger == null) {
377
            self::$logger = new Logger('Person');
378
        }
379
        self::$logger->debug('>>addToGroup(groupName=['.$groupName.'])');
380
381
        $group = new Rights();
382
        $group->loadByAttribute('name', $groupName);
383
384
        $lookup = $this->getPropObject('rights')->getLookup();
0 ignored issues
show
It seems like you code against a specific sub-type and not the parent class Alpha\Model\Type\Type as the method getLookup() does only exist in the following sub-classes of Alpha\Model\Type\Type: Alpha\Model\Type\Relation. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
385
        $lookup->setValue(array($this->getID(), $group->getID()));
386
        $lookup->save();
387
388
        self::$logger->debug('<<addToGroup');
389
    }
390
391
    /**
392
     * A generic method for mailing a person.
393
     *
394
     * @param string $message
395
     * @param string $subject
396
     *
397
     * @since 1.0
398
     *
399
     * @throws \Alpha\Exception\MailNotSentException
400
     */
401
    public function sendMail($message, $subject)
402
    {
403
        $config = ConfigProvider::getInstance();
404
405
        $body = '<html><head></head><body><p>Dear '.$this->getUsername().',</p>';
406
407
        $body .= $message;
408
409
        $body .= '</body></html>';
410
411
        $mailer = ServiceFactory::getInstance('Alpha\Util\Email\EmailProviderPHP', 'Alpha\Util\Email\EmailProviderInterface');
412
        $mailer->send($this->get('email'), $config->get('email.reply.to'), $subject, $body, true);
413
    }
414
415
    /**
416
     * Generates a random password for the user.
417
     *
418
     * @return string
419
     *
420
     * @since 1.0
421
     */
422
    public function generatePassword()
423
    {
424
        $alphabet = array('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z');
425
        // the password will be 7 random characters and 2 numbers
426
        $newPassword = '';
427
        for ($i = 0; $i < 7; ++$i) {
428
            $newPassword .= $alphabet[rand(0, 25)];
429
        }
430
        $newPassword .= rand(0, 100);
431
        $newPassword .= rand(0, 100);
432
433
        return $newPassword;
434
    }
435
436
    /**
437
     * Method for getting a count of the amount of article comments posted by the user.
438
     *
439
     * @return int
440
     *
441
     * @since 1.0
442
     */
443
    public function getCommentCount()
444
    {
445
        $temp = new ArticleComment();
446
447
        $sqlQuery = 'SELECT COUNT(ID) AS post_count FROM '.$temp->getTableName()." WHERE created_by='".$this->ID."';";
448
449
        $result = $this->query($sqlQuery);
450
451
        $row = $result[0];
452
453
        if (isset($row['post_count'])) {
454
            return $row['post_count'];
455
        } else {
456
            return 0;
457
        }
458
    }
459
}
460