Issues (103)

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.

src/User/Model/User.php (1 issue)

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
/*
4
 * This file is part of the 2amigos/yii2-usuario project.
5
 *
6
 * (c) 2amigOS! <http://2amigos.us/>
7
 *
8
 * For the full copyright and license information, please view
9
 * the LICENSE file that was distributed with this source code.
10
 */
11
12
namespace Da\User\Model;
13
14
use Da\User\Helper\SecurityHelper;
15
use Da\User\Query\UserQuery;
16
use Da\User\Traits\ContainerAwareTrait;
17
use Da\User\Traits\ModuleAwareTrait;
18
use Yii;
19
use yii\base\Exception;
20
use yii\base\InvalidConfigException;
21
use yii\base\InvalidParamException;
22
use yii\base\NotSupportedException;
23
use yii\behaviors\TimestampBehavior;
24
use yii\db\ActiveRecord;
25
use yii\helpers\ArrayHelper;
26
use yii\web\Application;
27
use yii\web\IdentityInterface;
28
29
/**
30
 * User ActiveRecord model.
31
 *
32
 * @property bool $isAdmin
33
 * @property bool $isBlocked
34
 * @property bool $isConfirmed      whether user account has been confirmed or not
35
 * @property bool $gdpr_deleted     whether user requested deletion of his account
36
 * @property bool $gdpr_consent     whether user has consent personal data processing
37
 *
38
 * Database fields:
39
 * @property int $id
40
 * @property string $username
41
 * @property string $email
42
 * @property string $unconfirmed_email
43
 * @property string $password_hash
44
 * @property string $auth_key
45
 * @property string $auth_tf_key
46
 * @property int $auth_tf_enabled
47
 * @property string $registration_ip
48
 * @property int $confirmed_at
49
 * @property int $blocked_at
50
 * @property int $flags
51
 * @property int $created_at
52
 * @property int $updated_at
53
 * @property int $last_login_at
54
 * @property int $gdpr_consent_date date of agreement of data processing
55
 * @property string $last_login_ip
56
 * @property int $password_changed_at
57
 * @property int $password_age
58
 * Defined relations:
59
 * @property SocialNetworkAccount[] $socialNetworkAccounts
60
 * @property Profile $profile
61
 */
62
class User extends ActiveRecord implements IdentityInterface
63
{
64
    use ModuleAwareTrait;
65
    use ContainerAwareTrait;
66
67
    // following constants are used on secured email changing process
68
    const OLD_EMAIL_CONFIRMED = 0b01;
69
    const NEW_EMAIL_CONFIRMED = 0b10;
70
71
    /**
72
     * @var string Plain password. Used for model validation
73
     */
74
    public $password;
75
    /**
76
     * @var array connected account list
77
     */
78
    protected $connectedAccounts;
79
80
    /**
81
     * {@inheritdoc}
82
     *
83
     * @throws InvalidParamException
84
     * @throws InvalidConfigException
85
     * @throws Exception
86
     */
87 16
    public static function tableName()
88
    {
89 16
        return '{{%user}}';
90
    }
91
92
    /**
93
     * {@inheritdoc}
94
     */
95 3
    public static function findIdentity($id)
96
    {
97 3
        return static::findOne($id);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return static::findOne($id); (yii\db\ActiveRecordInterface|array|null) is incompatible with the return type declared by the interface yii\web\IdentityInterface::findIdentity of type yii\web\IdentityInterface|null.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
98
    }
99
100
    /**
101
     * @return UserQuery
102
     */
103 16
    public static function find()
104
    {
105 16
        return new UserQuery(static::class);
106
    }
107
108
    /**
109
     * {@inheritdoc}
110
     *
111
     * @throws NotSupportedException
112
     */
113
    public static function findIdentityByAccessToken($token, $type = null)
114
    {
115
        throw new NotSupportedException('Method "' . __CLASS__ . '::' . __METHOD__ . '" is not implemented.');
116
    }
117
118
    /**
119
     * {@inheritdoc}
120
     */
121 10
    public function beforeSave($insert)
122
    {
123
        /** @var SecurityHelper $security */
124 10
        $security = $this->make(SecurityHelper::class);
125 10
        if ($insert) {
126 7
            $this->setAttribute('auth_key', $security->generateRandomString());
127 7
            if (Yii::$app instanceof Application) {
128 7
                $this->setAttribute('registration_ip', $this->module->disableIpLogging ? '127.0.0.1' : Yii::$app->request->getUserIP());
129
            }
130
        }
131
132 10
        if (!empty($this->password)) {
133 10
            $this->setAttribute(
134 10
                'password_hash',
135 10
                $security->generatePasswordHash($this->password, $this->getModule()->blowfishCost)
136
            );
137 10
            $this->password_changed_at = time();
138
        }
139
140 10
        return parent::beforeSave($insert);
141
    }
142
143
    /**
144
     * @inheritdoc
145
     *
146
     * @throws InvalidConfigException
147
     */
148 10
    public function afterSave($insert, $changedAttributes)
149
    {
150 10
        parent::afterSave($insert, $changedAttributes);
151
152 10
        if ($insert && $this->profile === null) {
153 7
            $profile = $this->make(Profile::class);
154 7
            $profile->link('user', $this);
155
        }
156 10
    }
157
158
    /**
159
     * {@inheritdoc}
160
     */
161 16
    public function behaviors()
162
    {
163
        $behaviors = [
164 16
            TimestampBehavior::class,
165
        ];
166
167 16
        if ($this->module->enableGdprCompliance) {
168 4
            $behaviors['GDPR'] = [
169
                'class' => TimestampBehavior::class,
170
                'createdAtAttribute' => 'gdpr_consent_date',
171
                'updatedAtAttribute' => false
172
            ];
173
        }
174
175 16
        return $behaviors;
176
    }
177
178
    /**
179
     * {@inheritdoc}
180
     */
181 2
    public function attributeLabels()
182
    {
183
        return [
184 2
            'username' => Yii::t('usuario', 'Username'),
185 2
            'email' => Yii::t('usuario', 'Email'),
186 2
            'registration_ip' => Yii::t('usuario', 'Registration IP'),
187 2
            'unconfirmed_email' => Yii::t('usuario', 'New email'),
188 2
            'password' => Yii::t('usuario', 'Password'),
189 2
            'created_at' => Yii::t('usuario', 'Registration time'),
190 2
            'confirmed_at' => Yii::t('usuario', 'Confirmation time'),
191 2
            'last_login_at' => Yii::t('usuario', 'Last login time'),
192 2
            'last_login_ip' => Yii::t('usuario', 'Last login IP'),
193 2
            'password_changed_at' => Yii::t('usuario', 'Last password change'),
194 2
            'password_age' => Yii::t('usuario', 'Password age'),
195
        ];
196
    }
197
198
    /**
199
     * {@inheritdoc}
200
     */
201 9
    public function scenarios()
202
    {
203 9
        return ArrayHelper::merge(
204 9
            parent::scenarios(),
205
            [
206 9
                'register' => ['username', 'email', 'password'],
207
                'connect' => ['username', 'email'],
208
                'create' => ['username', 'email', 'password'],
209
                'update' => ['username', 'email', 'password'],
210
                'settings' => ['username', 'email', 'password'],
211
            ]
212
        );
213
    }
214
215
    /**
216
     * {@inheritdoc}
217
     */
218 9
    public function rules()
219
    {
220
        return [
221
            // username rules
222 9
            'usernameRequired' => ['username', 'required', 'on' => ['register', 'create', 'connect', 'update']],
223
            'usernameMatch' => ['username', 'match', 'pattern' => '/^[-a-zA-Z0-9_\.@\+]+$/'],
224
            'usernameLength' => ['username', 'string', 'min' => 3, 'max' => 255],
225
            'usernameTrim' => ['username', 'trim'],
226
            'usernameUnique' => [
227 9
                'username',
228 9
                'unique',
229 9
                'message' => Yii::t('usuario', 'This username has already been taken'),
230
            ],
231
232
            // email rules
233
            'emailRequired' => ['email', 'required', 'on' => ['register', 'connect', 'create', 'update']],
234
            'emailPattern' => ['email', 'email'],
235
            'emailLength' => ['email', 'string', 'max' => 255],
236
            'emailUnique' => [
237 9
                'email',
238 9
                'unique',
239 9
                'message' => Yii::t('usuario', 'This email address has already been taken'),
240
            ],
241
            'emailTrim' => ['email', 'trim'],
242
243
            // password rules
244
            'passwordTrim' => ['password', 'trim'],
245
            'passwordRequired' => ['password', 'required', 'on' => ['register']],
246
            'passwordLength' => ['password', 'string', 'min' => 6, 'max' => 72, 'on' => ['register', 'create']],
247
248
            // two factor auth rules
249
            'twoFactorSecretTrim' => ['auth_tf_key', 'trim'],
250
            'twoFactorSecretLength' => ['auth_tf_key', 'string', 'max' => 16],
251
            'twoFactorEnabledNumber' => ['auth_tf_enabled', 'boolean']
252
        ];
253
    }
254
255
    /**
256
     * {@inheritdoc}
257
     */
258
    public function validateAuthKey($authKey)
259
    {
260
        return $this->getAttribute('auth_key') === $authKey;
261
    }
262
263
    /**
264
     * {@inheritdoc}
265
     */
266 9
    public function getId()
267
    {
268 9
        return $this->getAttribute('id');
269
    }
270
271
    /**
272
     * {@inheritdoc}
273
     */
274 9
    public function getAuthKey()
275
    {
276 9
        return $this->getAttribute('auth_key');
277
    }
278
279
    /**
280
     * @return bool whether is blocked or not
281
     */
282 4
    public function getIsBlocked()
283
    {
284 4
        return $this->blocked_at !== null;
285
    }
286
287
    /**
288
     * @throws InvalidConfigException
289
     * @return bool                   whether the user is an admin or not
290
     */
291 2
    public function getIsAdmin()
292
    {
293 2
        return $this->getAuth()->isAdmin($this->username);
294
    }
295
296
    /**
297
     * Returns whether user account has been confirmed or not.
298
     * @return bool whether user account has been confirmed or not
299
     */
300 5
    public function getIsConfirmed()
301
    {
302 5
        return $this->confirmed_at !== null;
303
    }
304
305
    /**
306
     * Checks whether a user has a specific role.
307
     *
308
     * @param string $role
309
     *
310
     * @return bool
311
     */
312
    public function hasRole($role)
313
    {
314
        return $this->getAuth()->hasRole($this->id, $role);
315
    }
316
317
    /**
318
     * @throws InvalidConfigException
319
     * @throws InvalidParamException
320
     * @return \yii\db\ActiveQuery
321
     */
322 10
    public function getProfile()
323
    {
324 10
        return $this->hasOne($this->getClassMap()->get(Profile::class), ['user_id' => 'id']);
325
    }
326
327
    /**
328
     * @throws \Exception
329
     * @return SocialNetworkAccount[] social connected accounts [ 'providerName' => socialAccountModel ]
330
     *
331
     */
332
    public function getSocialNetworkAccounts()
333
    {
334
        if (null === $this->connectedAccounts) {
335
            /** @var SocialNetworkAccount[] $accounts */
336
            $accounts = $this->hasMany(
337
                $this->getClassMap()
338
                    ->get(SocialNetworkAccount::class),
339
                ['user_id' => 'id']
340
            )
341
                ->all();
342
343
            foreach ($accounts as $account) {
344
                $this->connectedAccounts[$account->provider] = $account;
345
            }
346
        }
347
348
        return $this->connectedAccounts;
349
    }
350
351
    /**
352
     * Returns password age in days
353
     * @return integer
354
     */
355
    public function getPassword_age()
356
    {
357
        if (is_null($this->password_changed_at)) {
358
            return $this->getModule()->maxPasswordAge;
359
        }
360
        $d = new \DateTime("@{$this->password_changed_at}");
361
362
        return $d->diff(new \DateTime(), true)->format("%a");
363
    }
364
}
365