<?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\Contracts\Models\Identifiable;12use Pterodactyl\Models\Traits\HasAccessTokens;13use Illuminate\Auth\Passwords\CanResetPassword;14use Pterodactyl\Traits\Helpers\AvailableLanguages;15use Illuminate\Database\Eloquent\Relations\HasMany;16use Illuminate\Foundation\Auth\Access\Authorizable;17use Pterodactyl\Models\Traits\HasRealtimeIdentifier;18use Illuminate\Database\Eloquent\Factories\HasFactory;19use Illuminate\Database\Eloquent\Relations\MorphToMany;20use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;21use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract;22use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract;23use Pterodactyl\Notifications\SendPasswordReset as ResetPasswordNotification;2425/**26* Pterodactyl\Models\User.27*28* @property int $id29* @property string|null $external_id30* @property string $uuid31* @property string $username32* @property string $email33* @property string|null $name_first34* @property string|null $name_last35* @property string $password36* @property string|null $remember_token37* @property string $language38* @property bool $root_admin39* @property bool $use_totp40* @property string|null $totp_secret41* @property \Illuminate\Support\Carbon|null $totp_authenticated_at42* @property bool $gravatar43* @property \Illuminate\Support\Carbon|null $created_at44* @property \Illuminate\Support\Carbon|null $updated_at45* @property \Illuminate\Database\Eloquent\Collection|\Pterodactyl\Models\ApiKey[] $apiKeys46* @property int|null $api_keys_count47* @property string $name48* @property \Illuminate\Notifications\DatabaseNotificationCollection|\Illuminate\Notifications\DatabaseNotification[] $notifications49* @property int|null $notifications_count50* @property \Illuminate\Database\Eloquent\Collection|\Pterodactyl\Models\RecoveryToken[] $recoveryTokens51* @property int|null $recovery_tokens_count52* @property \Illuminate\Database\Eloquent\Collection|\Pterodactyl\Models\Server[] $servers53* @property int|null $servers_count54* @property \Illuminate\Database\Eloquent\Collection|\Pterodactyl\Models\UserSSHKey[] $sshKeys55* @property int|null $ssh_keys_count56* @property \Illuminate\Database\Eloquent\Collection|\Pterodactyl\Models\ApiKey[] $tokens57* @property int|null $tokens_count58*59* @method static \Database\Factories\UserFactory factory(...$parameters)60* @method static Builder|User newModelQuery()61* @method static Builder|User newQuery()62* @method static Builder|User query()63* @method static Builder|User whereCreatedAt($value)64* @method static Builder|User whereEmail($value)65* @method static Builder|User whereExternalId($value)66* @method static Builder|User whereGravatar($value)67* @method static Builder|User whereId($value)68* @method static Builder|User whereLanguage($value)69* @method static Builder|User whereNameFirst($value)70* @method static Builder|User whereNameLast($value)71* @method static Builder|User wherePassword($value)72* @method static Builder|User whereRememberToken($value)73* @method static Builder|User whereRootAdmin($value)74* @method static Builder|User whereTotpAuthenticatedAt($value)75* @method static Builder|User whereTotpSecret($value)76* @method static Builder|User whereUpdatedAt($value)77* @method static Builder|User whereUseTotp($value)78* @method static Builder|User whereUsername($value)79* @method static Builder|User whereUuid($value)80*81* @mixin \Eloquent82*/83#[Attributes\Identifiable('user')]84class User extends Model implements85AuthenticatableContract,86AuthorizableContract,87CanResetPasswordContract,88Identifiable89{90use Authenticatable;91use Authorizable;92use AvailableLanguages;93use CanResetPassword;94/** @use \Pterodactyl\Models\Traits\HasAccessTokens<\Pterodactyl\Models\ApiKey> */95use HasAccessTokens;96use Notifiable;97/** @use \Illuminate\Database\Eloquent\Factories\HasFactory<\Database\Factories\UserFactory> */98use HasFactory;99use HasRealtimeIdentifier;100101public const USER_LEVEL_USER = 0;102public const USER_LEVEL_ADMIN = 1;103104/**105* The resource name for this model when it is transformed into an106* API representation using fractal.107*/108public const RESOURCE_NAME = 'user';109110/**111* Level of servers to display when using access() on a user.112*/113protected string $accessLevel = 'all';114115/**116* The table associated with the model.117*/118protected $table = 'users';119120/**121* A list of mass-assignable variables.122*/123protected $fillable = [124'external_id',125'username',126'email',127'name_first',128'name_last',129'password',130'language',131'use_totp',132'totp_secret',133'totp_authenticated_at',134'gravatar',135'root_admin',136];137138/**139* Cast values to correct type.140*/141protected $casts = [142'root_admin' => 'boolean',143'use_totp' => 'boolean',144'gravatar' => 'boolean',145'totp_authenticated_at' => 'datetime',146];147148/**149* The attributes excluded from the model's JSON form.150*/151protected $hidden = ['password', 'remember_token', 'totp_secret', 'totp_authenticated_at'];152153/**154* Default values for specific fields in the database.155*/156protected $attributes = [157'external_id' => null,158'root_admin' => false,159'language' => 'en',160'use_totp' => false,161'totp_secret' => null,162];163164/**165* Rules verifying that the data being stored matches the expectations of the database.166*/167public static array $validationRules = [168'uuid' => 'required|string|size:36|unique:users,uuid',169'email' => 'required|email|between:1,191|unique:users,email',170'external_id' => 'sometimes|nullable|string|max:191|unique:users,external_id',171'username' => 'required|between:1,191|unique:users,username',172'name_first' => 'required|string|between:1,191',173'name_last' => 'required|string|between:1,191',174'password' => 'sometimes|nullable|string',175'root_admin' => 'boolean',176'language' => 'string',177'use_totp' => 'boolean',178'totp_secret' => 'nullable|string',179];180181/**182* Implement language verification by overriding Eloquence's gather183* rules function.184*/185public static function getRules(): array186{187$rules = parent::getRules();188189$rules['language'][] = new In(array_keys((new self())->getAvailableLanguages()));190$rules['username'][] = new Username();191192return $rules;193}194195/**196* Return the user model in a format that can be passed over to Vue templates.197*/198public function toVueObject(): array199{200return Collection::make($this->toArray())->except(['id', 'external_id'])201->merge(['identifier' => $this->identifier])202->toArray();203}204205/**206* Send the password reset notification.207*208* @param string $token209*/210public function sendPasswordResetNotification($token)211{212Activity::event('auth:reset-password')213->withRequestMetadata()214->subject($this)215->log('sending password reset email');216217$this->notify(new ResetPasswordNotification($token));218}219220/**221* Store the username as a lowercase string.222*/223public function setUsernameAttribute(string $value)224{225$this->attributes['username'] = mb_strtolower($value);226}227228/**229* Return a concatenated result for the accounts full name.230*/231public function getNameAttribute(): string232{233return trim($this->name_first . ' ' . $this->name_last);234}235236/**237* Returns all servers that a user owns.238*239* @return \Illuminate\Database\Eloquent\Relations\HasMany<\Pterodactyl\Models\Server, $this>240*/241public function servers(): HasMany242{243return $this->hasMany(Server::class, 'owner_id');244}245246/**247* @return \Illuminate\Database\Eloquent\Relations\HasMany<\Pterodactyl\Models\ApiKey, $this>248*/249public function apiKeys(): HasMany250{251return $this->hasMany(ApiKey::class)252->where('key_type', ApiKey::TYPE_ACCOUNT);253}254255/**256* @return \Illuminate\Database\Eloquent\Relations\HasMany<\Pterodactyl\Models\RecoveryToken, $this>257*/258public function recoveryTokens(): HasMany259{260return $this->hasMany(RecoveryToken::class);261}262263/**264* @return \Illuminate\Database\Eloquent\Relations\HasMany<\Pterodactyl\Models\UserSSHKey, $this>265*/266public function sshKeys(): HasMany267{268return $this->hasMany(UserSSHKey::class);269}270271/**272* Returns all the activity logs where this user is the subject — not to273* be confused by activity logs where this user is the _actor_.274*275* @return \Illuminate\Database\Eloquent\Relations\MorphToMany<\Pterodactyl\Models\ActivityLog, $this>276*/277public function activity(): MorphToMany278{279return $this->morphToMany(ActivityLog::class, 'subject', 'activity_log_subjects');280}281282/**283* Returns all the servers that a user can access by way of being the owner of the284* server, or because they are assigned as a subuser for that server.285*286* @return \Illuminate\Database\Eloquent\Builder<\Pterodactyl\Models\Server>287*/288public function accessibleServers(): Builder289{290return Server::query()291->select('servers.*')292->leftJoin('subusers', 'subusers.server_id', '=', 'servers.id')293->where(function (Builder $builder) {294$builder->where('servers.owner_id', $this->id)->orWhere('subusers.user_id', $this->id);295})296->groupBy('servers.id');297}298}299300301