Changeset View
Changeset View
Standalone View
Standalone View
src/app/User.php
<?php | <?php | ||||
namespace App; | namespace App; | ||||
use App\Entitlement; | use App\Entitlement; | ||||
use App\UserAlias; | use App\UserAlias; | ||||
use App\Sku; | use App\Sku; | ||||
use App\Traits\UserAliasesTrait; | use App\Traits\UserAliasesTrait; | ||||
use App\Traits\SettingsTrait; | use App\Traits\SettingsTrait; | ||||
use App\Wallet; | use App\Wallet; | ||||
use Illuminate\Database\Eloquent\SoftDeletes; | use Illuminate\Database\Eloquent\SoftDeletes; | ||||
use Illuminate\Support\Facades\Hash; | |||||
use Illuminate\Foundation\Auth\User as Authenticatable; | use Illuminate\Foundation\Auth\User as Authenticatable; | ||||
use Iatstuti\Database\Support\NullableFields; | use Iatstuti\Database\Support\NullableFields; | ||||
use Tymon\JWTAuth\Contracts\JWTSubject; | use Laravel\Passport\HasApiTokens; | ||||
use League\OAuth2\Server\Exception\OAuthServerException; | |||||
/** | /** | ||||
* The eloquent definition of a User. | * The eloquent definition of a User. | ||||
* | * | ||||
* @property string $email | * @property string $email | ||||
* @property int $id | * @property int $id | ||||
* @property string $password | * @property string $password | ||||
* @property int $status | * @property int $status | ||||
*/ | */ | ||||
class User extends Authenticatable implements JWTSubject | class User extends Authenticatable | ||||
{ | { | ||||
use NullableFields; | use NullableFields; | ||||
use UserAliasesTrait; | use UserAliasesTrait; | ||||
use SettingsTrait; | use SettingsTrait; | ||||
use SoftDeletes; | use SoftDeletes; | ||||
use HasApiTokens; | |||||
// a new user, default on creation | // a new user, default on creation | ||||
public const STATUS_NEW = 1 << 0; | public const STATUS_NEW = 1 << 0; | ||||
// it's been activated | // it's been activated | ||||
public const STATUS_ACTIVE = 1 << 1; | public const STATUS_ACTIVE = 1 << 1; | ||||
// user has been suspended | // user has been suspended | ||||
public const STATUS_SUSPENDED = 1 << 2; | public const STATUS_SUSPENDED = 1 << 2; | ||||
// user has been deleted | // user has been deleted | ||||
▲ Show 20 Lines • Show All 331 Lines • ▼ Show 20 Lines | class User extends Authenticatable | ||||
* Helper to find user by email address, whether it is | * Helper to find user by email address, whether it is | ||||
* main email address, alias or an external email. | * main email address, alias or an external email. | ||||
* | * | ||||
* If there's more than one alias NULL will be returned. | * If there's more than one alias NULL will be returned. | ||||
* | * | ||||
* @param string $email Email address | * @param string $email Email address | ||||
* @param bool $external Search also for an external email | * @param bool $external Search also for an external email | ||||
* | * | ||||
* @return \App\User User model object if found | * @return \App\User|null User model object if found | ||||
*/ | */ | ||||
public static function findByEmail(string $email, bool $external = false): ?User | public static function findByEmail(string $email, bool $external = false): ?User | ||||
{ | { | ||||
if (strpos($email, '@') === false) { | if (strpos($email, '@') === false) { | ||||
return null; | return null; | ||||
} | } | ||||
$email = \strtolower($email); | $email = \strtolower($email); | ||||
Show All 10 Lines | public static function findByEmail(string $email, bool $external = false): ?User | ||||
return $aliases->first()->user; | return $aliases->first()->user; | ||||
} | } | ||||
// TODO: External email | // TODO: External email | ||||
return null; | return null; | ||||
} | } | ||||
public function getJWTIdentifier() | |||||
{ | |||||
return $this->getKey(); | |||||
} | |||||
public function getJWTCustomClaims() | |||||
{ | |||||
return []; | |||||
} | |||||
/** | /** | ||||
* Return groups controlled by the current user. | * Return groups controlled by the current user. | ||||
* | * | ||||
* @return \Illuminate\Database\Eloquent\Builder Query builder | * @return \Illuminate\Database\Eloquent\Builder Query builder | ||||
*/ | */ | ||||
public function groups() | public function groups() | ||||
{ | { | ||||
▲ Show 20 Lines • Show All 292 Lines • ▼ Show 20 Lines | public function setStatusAttribute($status) | ||||
} | } | ||||
if ($status > 0) { | if ($status > 0) { | ||||
throw new \Exception("Invalid user status: {$status}"); | throw new \Exception("Invalid user status: {$status}"); | ||||
} | } | ||||
$this->attributes['status'] = $new_status; | $this->attributes['status'] = $new_status; | ||||
} | } | ||||
/** | |||||
* Retrieve and authenticate a user | |||||
* | |||||
* @param string $username The username. | |||||
* @param string $password The password in plain text. | |||||
* @param string $secondFactor The second factor (secondfactor from current request is used as fallback). | |||||
* | |||||
* @return array ['user', 'reason', 'errorMessage'] | |||||
*/ | |||||
public static function findAndAuthenticate($username, $password, $secondFactor = null): ?array | |||||
{ | |||||
$user = User::where('email', $username)->first(); | |||||
if (!$user) { | |||||
return ['reason' => 'notfound', 'errorMessage' => "User not found."]; | |||||
machniak: I think we can remove this error logging here and below. | |||||
} | |||||
Done Inline ActionsIs this user facing error message? Should we localize it? machniak: Is this user facing error message? Should we localize it? | |||||
Done Inline ActionsOnly in the case of secondfactor error, which is already localized. I don't think we should be localizing error messages in general (that should be part of the UI layer), so I wouldn't localize these for the time being. mollekopf: Only in the case of secondfactor error, which is already localized.
I don't think we should be… | |||||
if (empty($password)) { | |||||
return ['reason' => 'credentials', 'errorMessage' => "Invalid password."]; | |||||
} | |||||
$result = Hash::check($password, $user->password); | |||||
if (!$result) { | |||||
return ['reason' => 'credentials', 'errorMessage' => "Invalid password."]; | |||||
} | |||||
if (!$secondFactor) { | |||||
// Check the request if there is a second factor provided | |||||
// as fallback. | |||||
$secondFactor = request()->secondfactor; | |||||
} | |||||
try { | |||||
(new \App\Auth\SecondFactor($user))->validate($secondFactor); | |||||
} catch (\Exception $e) { | |||||
return ['reason' => 'secondfactor', 'errorMessage' => $e->getMessage()]; | |||||
} | |||||
return ['user' => $user]; | |||||
} | |||||
/** | |||||
* Hook for passport | |||||
* | |||||
* @throws \OAuthServerException | |||||
* | |||||
* @return \App\User User model object if found | |||||
*/ | |||||
public function findAndValidateForPassport($username, $password): User | |||||
{ | |||||
$result = self::findAndAuthenticate($username, $password); | |||||
if (isset($result['reason'])) { | |||||
if ($result['reason'] == 'secondfactor') { | |||||
// This results in a json response of {'error': 'secondfactor', 'error_description': '$errorMessage'} | |||||
throw new OAuthServerException($result['errorMessage'], 6, 'secondfactor', 401); | |||||
} | |||||
throw OAuthServerException::invalidCredentials(); | |||||
} | |||||
return $result['user']; | |||||
} | |||||
} | } |
I think we can remove this error logging here and below.