You've already forked Atomcms-edit
7f59024bef
- Remove User::$guarded = [] to prevent mass assignment attacks - Enable SQL strict mode and disable emulated prepares (SQL injection prevention) - Switch password hashing from bcrypt to argon2id (stronger algorithm) - Enable session encryption to protect session data at rest - Restrict TrustProxies to localhost only (prevent IP spoofing) - Restrict CORS allowed_methods via env variable instead of wildcard - Add PayPal amount mismatch detection to prevent payment manipulation - Add double-capture prevention (idempotency check) - Add expected_amount column to transactions table for verification
422 lines
13 KiB
PHP
Executable File
422 lines
13 KiB
PHP
Executable File
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Models;
|
|
|
|
use App\Models\Articles\WebsiteArticle;
|
|
use App\Models\Articles\WebsiteArticleComment;
|
|
use App\Models\Community\Staff\WebsiteStaffApplications;
|
|
use App\Models\Community\Staff\WebsiteTeam;
|
|
use App\Models\Game\Furniture\Item;
|
|
use App\Models\Game\Guild\GuildMember;
|
|
use App\Models\Game\Permission;
|
|
use App\Models\Game\Player\MessengerFriendship;
|
|
use App\Models\Game\Player\UserBadge;
|
|
use App\Models\Game\Player\UserCurrency;
|
|
use App\Models\Game\Player\UserSetting;
|
|
use App\Models\Game\Player\UserSubscription;
|
|
use App\Models\Game\Room;
|
|
use App\Models\Help\WebsiteHelpCenterTicket;
|
|
use App\Models\Miscellaneous\CameraWeb;
|
|
use App\Models\Miscellaneous\WebsiteBetaCode;
|
|
use App\Models\Shop\WebsitePaypalTransaction;
|
|
use App\Models\Shop\WebsiteUsedShopVoucher;
|
|
use App\Models\User\Ban;
|
|
use App\Models\User\ClaimedReferralLog;
|
|
use App\Models\User\Referral;
|
|
use App\Models\User\UserReferral;
|
|
use App\Models\User\WebsiteUserGuestbook;
|
|
use Filament\Models\Contracts\FilamentUser;
|
|
use Filament\Models\Contracts\HasName;
|
|
use Filament\Panel;
|
|
use Illuminate\Database\Eloquent\Collection;
|
|
use Illuminate\Database\Eloquent\Factories\Factory;
|
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
|
use Illuminate\Database\Eloquent\Relations\HasOne;
|
|
use Illuminate\Foundation\Auth\User as Authenticatable;
|
|
use Illuminate\Notifications\Notifiable;
|
|
use Illuminate\Support\Facades\Hash;
|
|
use Illuminate\Support\Str;
|
|
use Laravel\Fortify\TwoFactorAuthenticatable;
|
|
use Laravel\Fortify\TwoFactorAuthenticationProvider;
|
|
use Laravel\Sanctum\HasApiTokens;
|
|
use Spatie\Activitylog\LogOptions;
|
|
use Spatie\Activitylog\Traits\LogsActivity;
|
|
|
|
/**
|
|
* @method static \Illuminate\Database\Eloquent\Builder|User query()
|
|
* @method static \Illuminate\Database\Eloquent\Builder|User where($column, $operator = null, $value = null)
|
|
* @method static \Illuminate\Database\Eloquent\Builder|User orWhere($column, $operator = null, $value = null)
|
|
* @method static \Illuminate\Database\Eloquent\Builder|User find($id, $columns = ['*'])
|
|
* @method static \Illuminate\Database\Eloquent\Builder|User first($columns = ['*'])
|
|
* @method static \Illuminate\Database\Eloquent\Builder|User create(array $attributes)
|
|
* @method static \Illuminate\Database\Eloquent\Builder|User updateOrCreate(array $attributes, array $values = [])
|
|
* @method static \Illuminate\Database\Eloquent\Builder|User select($columns = ['*'])
|
|
* @method static \Illuminate\Database\Eloquent\Builder|User count($columns = '*')
|
|
* @method static \Illuminate\Database\Eloquent\Builder|User get($columns = ['*'])
|
|
*
|
|
* @property int $id
|
|
* @property string $username
|
|
* @property string $mail
|
|
* @property string $password
|
|
* @property string $account_created
|
|
* @property string|null $last_login
|
|
* @property string|null $last_online
|
|
* @property string|null $motto
|
|
* @property string $look
|
|
* @property int $credits
|
|
* @property int $rank
|
|
* @property bool $online
|
|
* @property string $auth_ticket
|
|
* @property string $ip_register
|
|
* @property string $ip_current
|
|
* @property int $home_room
|
|
* @property string|null $referral_code
|
|
* @property string|null $two_factor_secret
|
|
* @property string|null $two_factor_recovery_codes
|
|
* @property string|null $two_factor_confirmed_at
|
|
* @property bool|null $hidden_staff
|
|
* @property int|null $team_id
|
|
* @property int|null $website_balance
|
|
* @property int $radio_points
|
|
* @property array|null $preferences
|
|
* @property-read UserSetting|null $settings
|
|
* @property-read Collection<int, UserCurrency> $currencies
|
|
* @property-read Ban|null $ban
|
|
* @property-read Permission $permission
|
|
* @property-read UserReferral|null $referrals
|
|
* @property-read Collection<int, Referral> $userReferrals
|
|
* @property-read Collection<int, ClaimedReferralLog> $claimedReferralLog
|
|
* @property-read Collection<int, UserBadge> $badges
|
|
* @property-read Collection<int, Room> $rooms
|
|
* @property-read Collection<int, MessengerFriendship> $friends
|
|
* @property-read Ban|null $banRelation
|
|
* @property-read WebsiteBetaCode|null $betaCode
|
|
* @property-read WebsiteTeam|null $team
|
|
* @property-read Collection<int, WebsiteStaffApplications> $applications
|
|
* @property-read UserSubscription|null $hcSubscription
|
|
* @property-read Collection<int, WebsiteArticleComment> $articleComments
|
|
* @property-read Collection<int, WebsitePaypalTransaction> $transactions
|
|
* @property-read Collection<int, WebsiteUsedShopVoucher> $usedShopVouchers
|
|
* @property-read Collection<int, Item> $items
|
|
* @property-read Collection<int, WebsiteHelpCenterTicket> $tickets
|
|
* @property-read Collection<int, CameraWeb> $photos
|
|
* @property-read Collection<int, WebsiteUserGuestbook> $profileGuestbook
|
|
* @property-read Collection<int, WebsiteUserGuestbook> $guestbook
|
|
* @property-read Collection<int, ChatlogRoom> $chatLogs
|
|
* @property-read Collection<int, ChatlogPrivate> $chatLogsPrivate
|
|
* @property-read Collection<int, WebsiteArticle> $articles
|
|
* @property-read Collection<int, Session> $sessions
|
|
*
|
|
* @extends Factory<User>
|
|
*/
|
|
class User extends Authenticatable implements FilamentUser, HasName
|
|
{
|
|
use HasApiTokens;
|
|
use HasFactory;
|
|
use LogsActivity;
|
|
use Notifiable;
|
|
use TwoFactorAuthenticatable;
|
|
|
|
#[\Override]
|
|
public $timestamps = false;
|
|
|
|
#[\Override]
|
|
protected $fillable = ['username', 'mail', 'password', 'account_created', 'last_login', 'motto', 'look', 'credits', 'auth_ticket', 'home_room', 'ip_register', 'ip_current', 'referral_code', 'preferences', 'team_id', 'avatar_background', 'home_background', 'pincode', 'secret_key', 'extra_rank', 'is_hidden', 'background_id', 'background_stand_id', 'background_overlay_id', 'radio_points', 'pixels', 'points', 'online', 'gender', 'rank', 'mail_verified', 'two_factor_secret', 'two_factor_recovery_codes', 'two_factor_confirmed_at'];
|
|
|
|
#[\Override]
|
|
protected $hidden = ['id', 'password', 'remember_token'];
|
|
|
|
/**
|
|
* @return array<string, string>
|
|
*/
|
|
#[\Override]
|
|
protected function casts(): array
|
|
{
|
|
return [
|
|
'email_verified_at' => 'datetime',
|
|
'password' => 'hashed',
|
|
'hidden_staff' => 'boolean',
|
|
'online' => 'boolean',
|
|
'preferences' => 'array',
|
|
];
|
|
}
|
|
|
|
public function currencies(): HasMany
|
|
{
|
|
return $this->hasMany(UserCurrency::class, 'user_id');
|
|
}
|
|
|
|
public function sessions(): HasMany
|
|
{
|
|
return $this->hasMany(Session::class);
|
|
}
|
|
|
|
public function currency(string $currency): int
|
|
{
|
|
if (! $this->relationLoaded('currencies')) {
|
|
$this->load('currencies');
|
|
}
|
|
|
|
$type = match ($currency) {
|
|
'duckets' => 0,
|
|
'diamonds' => 5,
|
|
'points' => 101,
|
|
default => 0,
|
|
};
|
|
|
|
return $this->currencies->where('type', $type)->first()->amount ?? 0;
|
|
}
|
|
|
|
public function permission(): HasOne
|
|
{
|
|
return $this->hasOne(Permission::class, 'id', 'rank');
|
|
}
|
|
|
|
public function articles(): HasMany
|
|
{
|
|
return $this->hasMany(WebsiteArticle::class);
|
|
}
|
|
|
|
public function referrals(): HasOne
|
|
{
|
|
return $this->hasOne(UserReferral::class);
|
|
}
|
|
|
|
public function userReferrals(): HasMany
|
|
{
|
|
return $this->hasMany(Referral::class);
|
|
}
|
|
|
|
public function claimedReferralLog(): HasMany
|
|
{
|
|
return $this->hasMany(ClaimedReferralLog::class);
|
|
}
|
|
|
|
public function badges(): HasMany
|
|
{
|
|
return $this->hasMany(UserBadge::class);
|
|
}
|
|
|
|
public function rooms(): HasMany
|
|
{
|
|
return $this->hasMany(Room::class, 'owner_id');
|
|
}
|
|
|
|
public function friends(): HasMany
|
|
{
|
|
return $this->hasMany(MessengerFriendship::class, 'user_one_id');
|
|
}
|
|
|
|
public function referralsNeeded(): int
|
|
{
|
|
$referrals = $this->referrals?->referrals_total ?? 0;
|
|
|
|
return (int) setting('referrals_needed') - (int) $referrals;
|
|
}
|
|
|
|
public function ban(): HasOne
|
|
{
|
|
return $this->hasOne(Ban::class, 'user_id')
|
|
->where('ban_expire', '>', time())
|
|
->whereIn('type', ['account', 'super'])
|
|
->withoutGlobalScopes();
|
|
}
|
|
|
|
public function settings(): HasOne
|
|
{
|
|
return $this->hasOne(UserSetting::class);
|
|
}
|
|
|
|
public function ssoTicket(): string
|
|
{
|
|
$hotelName = Str::replace(' ', '', (string) setting('hotel_name'));
|
|
$sso = sprintf('%s-%s', $hotelName, Str::uuid()->toString());
|
|
|
|
$this->update([
|
|
'auth_ticket' => $sso,
|
|
'ip_current' => request()->ip(),
|
|
]);
|
|
|
|
return $sso;
|
|
}
|
|
|
|
public function betaCode(): HasOne
|
|
{
|
|
return $this->hasOne(WebsiteBetaCode::class);
|
|
}
|
|
|
|
public function team(): BelongsTo
|
|
{
|
|
return $this->belongsTo(WebsiteTeam::class, 'team_id');
|
|
}
|
|
|
|
public function applications(): HasMany
|
|
{
|
|
return $this->hasMany(WebsiteStaffApplications::class, 'user_id');
|
|
}
|
|
|
|
public function hcSubscription(): HasOne
|
|
{
|
|
return $this->hasOne(UserSubscription::class);
|
|
}
|
|
|
|
public function articleComments(): HasMany
|
|
{
|
|
return $this->hasMany(WebsiteArticleComment::class);
|
|
}
|
|
|
|
public function transactions(): HasMany
|
|
{
|
|
return $this->hasMany(WebsitePaypalTransaction::class);
|
|
}
|
|
|
|
public function usedShopVouchers(): HasMany
|
|
{
|
|
return $this->hasMany(WebsiteUsedShopVoucher::class);
|
|
}
|
|
|
|
public function items(): HasMany
|
|
{
|
|
return $this->hasMany(Item::class, 'user_id');
|
|
}
|
|
|
|
public function tickets(): HasMany
|
|
{
|
|
return $this->hasMany(WebsiteHelpCenterTicket::class);
|
|
}
|
|
|
|
public function photos(): HasMany
|
|
{
|
|
return $this->hasMany(CameraWeb::class);
|
|
}
|
|
|
|
public function guilds(): HasMany
|
|
{
|
|
return $this->hasMany(GuildMember::class, 'user_id');
|
|
}
|
|
|
|
public function profileGuestbook(): HasMany
|
|
{
|
|
return $this->hasMany(WebsiteUserGuestbook::class, 'profile_id');
|
|
}
|
|
|
|
public function guestbook(): HasMany
|
|
{
|
|
return $this->hasMany(WebsiteUserGuestbook::class, 'user_id');
|
|
}
|
|
|
|
public function chatLogs(): HasMany
|
|
{
|
|
return $this->hasMany(ChatlogRoom::class, 'user_from_id');
|
|
}
|
|
|
|
public function chatLogsPrivate(): HasMany
|
|
{
|
|
return $this->hasMany(ChatlogPrivate::class, 'user_from_id');
|
|
}
|
|
|
|
public function socialAccounts(): HasMany
|
|
{
|
|
return $this->hasMany(SocialAccount::class);
|
|
}
|
|
|
|
public function hasSocialAccount(string $provider): bool
|
|
{
|
|
return $this->socialAccounts()->where('provider', $provider)->exists();
|
|
}
|
|
|
|
public function getOnlineFriends(int $total = 10): Collection
|
|
{
|
|
return $this->friends()
|
|
->select(['user_two_id', 'users.id', 'users.username', 'users.look', 'users.motto', 'users.last_online'])
|
|
->join('users', 'users.id', '=', 'user_two_id')
|
|
->where('users.online', '1')
|
|
->inRandomOrder()
|
|
->limit($total)
|
|
->get()
|
|
->map(fn ($item) => (new User)->forceFill([
|
|
'id' => $item->id,
|
|
'username' => $item->username,
|
|
'look' => $item->look,
|
|
'motto' => $item->motto,
|
|
'last_online' => $item->last_online,
|
|
]));
|
|
}
|
|
|
|
public function confirmTwoFactorAuthentication(string $code): bool
|
|
{
|
|
$secret = $this->two_factor_secret;
|
|
if ($secret === null) {
|
|
return false;
|
|
}
|
|
|
|
$codeIsValid = app(TwoFactorAuthenticationProvider::class)
|
|
->verify(decrypt((string) $secret), $code);
|
|
|
|
if (! $codeIsValid) {
|
|
return false;
|
|
}
|
|
|
|
$this->update(['two_factor_confirmed' => true]);
|
|
|
|
return true;
|
|
}
|
|
|
|
public function hasAppliedForPosition(int $rankId): bool
|
|
{
|
|
return $this->applications()->where('rank_id', $rankId)->exists();
|
|
}
|
|
|
|
public function changePassword(string $newPassword): void
|
|
{
|
|
$this->password = Hash::make($newPassword);
|
|
$this->save();
|
|
}
|
|
|
|
public function getFilamentName(): string
|
|
{
|
|
return $this->username ?? 'Guest';
|
|
}
|
|
|
|
public function canAccessPanel(Panel $panel): bool
|
|
{
|
|
return hasHousekeepingPermission('can_access_housekeeping');
|
|
}
|
|
|
|
public function getActivitylogOptions(): LogOptions
|
|
{
|
|
return LogOptions::defaults()
|
|
->logOnly(['id', 'username', 'motto', 'rank', 'credits'])
|
|
->logOnlyDirty()
|
|
->dontSubmitEmptyLogs();
|
|
}
|
|
|
|
/**
|
|
* @param array<string, mixed> $options
|
|
*/
|
|
#[\Override]
|
|
public function save(array $options = []): bool
|
|
{
|
|
if (! $this->isDirty()) {
|
|
return false;
|
|
}
|
|
|
|
return parent::save($options);
|
|
}
|
|
|
|
public function hasAppliedForTeam(int $teamId): bool
|
|
{
|
|
if ($teamId === 0) {
|
|
return false;
|
|
}
|
|
|
|
return $this->applications()
|
|
->where('team_id', $teamId)
|
|
->exists();
|
|
}
|
|
}
|