<?php12namespace Pterodactyl\Models;34use Pterodactyl\Rules\Username;5use Pterodactyl\Facades\Activity;6use Illuminate\Support\Collection;7use Illuminate\Validation\Rules\In;8use Illuminate\Auth\Authenticatable;9use Illuminate\Notifications\Notifiable;10use Illuminate\Database\Eloquent\Builder;11use Pterodactyl\Models\Traits\HasAccessTokens;12use Illuminate\Auth\Passwords\CanResetPassword;13use Pterodactyl\Traits\Helpers\AvailableLanguages;14use Illuminate\Database\Eloquent\Relations\HasMany;15use Illuminate\Foundation\Auth\Access\Authorizable;16use Illuminate\Database\Eloquent\Factories\HasFactory;17use Illuminate\Database\Eloquent\Relations\MorphToMany;18use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;19use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract;20use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract;21use Pterodactyl\Notifications\SendPasswordReset as ResetPasswordNotification;2223/**24* Pterodactyl\Models\User.25*26* @property int $id27* @property string|null $external_id28* @property string $uuid29* @property string $username30* @property string $email31* @property string|null $name_first32* @property string|null $name_last33* @property string $password34* @property string|null $remember_token35* @property string $language36* @property bool $root_admin37* @property bool $use_totp38* @property string|null $totp_secret39* @property \Illuminate\Support\Carbon|null $totp_authenticated_at40* @property bool $gravatar41* @property \Illuminate\Support\Carbon|null $created_at42* @property \Illuminate\Support\Carbon|null $updated_at43* @property \Illuminate\Database\Eloquent\Collection|\Pterodactyl\Models\ApiKey[] $apiKeys44* @property int|null $api_keys_count45* @property string $name46* @property \Illuminate\Notifications\DatabaseNotificationCollection|\Illuminate\Notifications\DatabaseNotification[] $notifications47* @property int|null $notifications_count48* @property \Illuminate\Database\Eloquent\Collection|\Pterodactyl\Models\RecoveryToken[] $recoveryTokens49* @property int|null $recovery_tokens_count50* @property \Illuminate\Database\Eloquent\Collection|\Pterodactyl\Models\Server[] $servers51* @property int|null $servers_count52* @property \Illuminate\Database\Eloquent\Collection|\Pterodactyl\Models\UserSSHKey[] $sshKeys53* @property int|null $ssh_keys_count54* @property \Illuminate\Database\Eloquent\Collection|\Pterodactyl\Models\ApiKey[] $tokens55* @property int|null $tokens_count56*57* @method static \Database\Factories\UserFactory factory(...$parameters)58* @method static Builder|User newModelQuery()59* @method static Builder|User newQuery()60* @method static Builder|User query()61* @method static Builder|User whereCreatedAt($value)62* @method static Builder|User whereEmail($value)63* @method static Builder|User whereExternalId($value)64* @method static Builder|User whereGravatar($value)65* @method static Builder|User whereId($value)66* @method static Builder|User whereLanguage($value)67* @method static Builder|User whereNameFirst($value)68* @method static Builder|User whereNameLast($value)69* @method static Builder|User wherePassword($value)70* @method static Builder|User whereRememberToken($value)71* @method static Builder|User whereRootAdmin($value)72* @method static Builder|User whereTotpAuthenticatedAt($value)73* @method static Builder|User whereTotpSecret($value)74* @method static Builder|User whereUpdatedAt($value)75* @method static Builder|User whereUseTotp($value)76* @method static Builder|User whereUsername($value)77* @method static Builder|User whereUuid($value)78*79* @mixin \Eloquent80*/81class User extends Model implements82AuthenticatableContract,83AuthorizableContract,84CanResetPasswordContract85{86use Authenticatable;87use Authorizable;88use AvailableLanguages;89use CanResetPassword;90use HasAccessTokens;91use Notifiable;92/** @use HasFactory<\Database\Factories\UserFactory> */93use HasFactory;9495public const USER_LEVEL_USER = 0;96public const USER_LEVEL_ADMIN = 1;9798/**99* The resource name for this model when it is transformed into an100* API representation using fractal.101*/102public const RESOURCE_NAME = 'user';103104/**105* Level of servers to display when using access() on a user.106*/107protected string $accessLevel = 'all';108109/**110* The table associated with the model.111*/112protected $table = 'users';113114/**115* A list of mass-assignable variables.116*/117protected $fillable = [118'external_id',119'username',120'email',121'name_first',122'name_last',123'password',124'language',125'use_totp',126'totp_secret',127'totp_authenticated_at',128'gravatar',129'root_admin',130];131132/**133* Cast values to correct type.134*/135protected $casts = [136'root_admin' => 'boolean',137'use_totp' => 'boolean',138'gravatar' => 'boolean',139'totp_authenticated_at' => 'datetime',140];141142/**143* The attributes excluded from the model's JSON form.144*/145protected $hidden = ['password', 'remember_token', 'totp_secret', 'totp_authenticated_at'];146147/**148* Default values for specific fields in the database.149*/150protected $attributes = [151'external_id' => null,152'root_admin' => false,153'language' => 'en',154'use_totp' => false,155'totp_secret' => null,156];157158/**159* Rules verifying that the data being stored matches the expectations of the database.160*/161public static array $validationRules = [162'uuid' => 'required|string|size:36|unique:users,uuid',163'email' => 'required|email|between:1,191|unique:users,email',164'external_id' => 'sometimes|nullable|string|max:191|unique:users,external_id',165'username' => 'required|between:1,191|unique:users,username',166'name_first' => 'required|string|between:1,191',167'name_last' => 'required|string|between:1,191',168'password' => 'sometimes|nullable|string',169'root_admin' => 'boolean',170'language' => 'string',171'use_totp' => 'boolean',172'totp_secret' => 'nullable|string',173];174175/**176* Implement language verification by overriding Eloquence's gather177* rules function.178*/179public static function getRules(): array180{181$rules = parent::getRules();182183$rules['language'][] = new In(array_keys((new self())->getAvailableLanguages()));184$rules['username'][] = new Username();185186return $rules;187}188189/**190* Return the user model in a format that can be passed over to Vue templates.191*/192public function toVueObject(): array193{194return Collection::make($this->toArray())->except(['id', 'external_id'])->toArray();195}196197/**198* Send the password reset notification.199*200* @param string $token201*/202public function sendPasswordResetNotification($token)203{204Activity::event('auth:reset-password')205->withRequestMetadata()206->subject($this)207->log('sending password reset email');208209$this->notify(new ResetPasswordNotification($token));210}211212/**213* Store the username as a lowercase string.214*/215public function setUsernameAttribute(string $value)216{217$this->attributes['username'] = mb_strtolower($value);218}219220/**221* Return a concatenated result for the accounts full name.222*/223public function getNameAttribute(): string224{225return trim($this->name_first . ' ' . $this->name_last);226}227228/**229* Returns all servers that a user owns.230*/231public function servers(): HasMany232{233return $this->hasMany(Server::class, 'owner_id');234}235236public function apiKeys(): HasMany237{238return $this->hasMany(ApiKey::class)239->where('key_type', ApiKey::TYPE_ACCOUNT);240}241242public function recoveryTokens(): HasMany243{244return $this->hasMany(RecoveryToken::class);245}246247public function sshKeys(): HasMany248{249return $this->hasMany(UserSSHKey::class);250}251252/**253* Returns all the activity logs where this user is the subject — not to254* be confused by activity logs where this user is the _actor_.255*/256public function activity(): MorphToMany257{258return $this->morphToMany(ActivityLog::class, 'subject', 'activity_log_subjects');259}260261/**262* Returns all the servers that a user can access by way of being the owner of the263* server, or because they are assigned as a subuser for that server.264*/265public function accessibleServers(): Builder266{267return Server::query()268->select('servers.*')269->leftJoin('subusers', 'subusers.server_id', '=', 'servers.id')270->where(function (Builder $builder) {271$builder->where('servers.owner_id', $this->id)->orWhere('subusers.user_id', $this->id);272})273->groupBy('servers.id');274}275}276277278