2022-06-10 10:47:46 +00:00
|
|
|
<?php
|
|
|
|
|
|
|
|
namespace App\Services;
|
|
|
|
|
2023-08-20 22:35:58 +00:00
|
|
|
use App\Exceptions\UserProspectUpdateDeniedException;
|
2024-03-30 16:49:25 +00:00
|
|
|
use App\Facades\License;
|
2022-06-10 10:47:46 +00:00
|
|
|
use App\Models\User;
|
2024-03-30 16:49:25 +00:00
|
|
|
use App\Repositories\UserRepository;
|
2024-03-31 17:19:03 +00:00
|
|
|
use App\Values\SSOUser;
|
2022-06-10 10:47:46 +00:00
|
|
|
use Illuminate\Contracts\Hashing\Hasher;
|
2024-03-19 22:48:12 +00:00
|
|
|
use Illuminate\Support\Facades\File;
|
|
|
|
use Illuminate\Support\Str;
|
2022-06-10 10:47:46 +00:00
|
|
|
|
|
|
|
class UserService
|
|
|
|
{
|
2024-03-30 16:49:25 +00:00
|
|
|
public function __construct(
|
2024-04-18 14:36:28 +00:00
|
|
|
private readonly UserRepository $repository,
|
|
|
|
private readonly Hasher $hash,
|
|
|
|
private readonly ImageWriter $imageWriter
|
2024-03-30 16:49:25 +00:00
|
|
|
) {
|
2022-06-10 10:47:46 +00:00
|
|
|
}
|
|
|
|
|
2024-03-30 16:49:25 +00:00
|
|
|
/** @noinspection PhpIncompatibleReturnTypeInspection */
|
|
|
|
public function createUser(
|
|
|
|
string $name,
|
|
|
|
string $email,
|
|
|
|
string $plainTextPassword,
|
|
|
|
bool $isAdmin,
|
|
|
|
?string $avatar = null,
|
|
|
|
?string $ssoId = null,
|
|
|
|
?string $ssoProvider = null,
|
|
|
|
): User {
|
|
|
|
if ($ssoProvider) {
|
|
|
|
License::requirePlus();
|
2024-03-31 17:19:03 +00:00
|
|
|
SSOUser::assertValidProvider($ssoProvider);
|
2024-03-30 16:49:25 +00:00
|
|
|
}
|
|
|
|
|
2022-08-09 18:45:11 +00:00
|
|
|
return User::query()->create([
|
2022-06-10 10:47:46 +00:00
|
|
|
'name' => $name,
|
|
|
|
'email' => $email,
|
2024-03-30 16:49:25 +00:00
|
|
|
'password' => $plainTextPassword ? $this->hash->make($plainTextPassword) : '',
|
2022-06-10 10:47:46 +00:00
|
|
|
'is_admin' => $isAdmin,
|
2024-03-30 16:49:25 +00:00
|
|
|
'sso_id' => $ssoId,
|
|
|
|
'sso_provider' => $ssoProvider,
|
2024-04-24 20:44:47 +00:00
|
|
|
'avatar' => $avatar ? $this->createNewAvatar($avatar) : null,
|
2022-06-10 10:47:46 +00:00
|
|
|
]);
|
|
|
|
}
|
|
|
|
|
2024-03-31 17:19:03 +00:00
|
|
|
public function createOrUpdateUserFromSSO(SSOUser $ssoUser): User
|
2024-03-30 16:49:25 +00:00
|
|
|
{
|
|
|
|
License::requirePlus();
|
|
|
|
|
2024-03-31 17:19:03 +00:00
|
|
|
$existingUser = $this->repository->findOneBySSO($ssoUser);
|
2024-03-30 16:49:25 +00:00
|
|
|
|
|
|
|
if ($existingUser) {
|
|
|
|
$existingUser->update([
|
2024-03-31 17:19:03 +00:00
|
|
|
'avatar' => $existingUser->has_custom_avatar ? $existingUser->avatar : $ssoUser->avatar,
|
|
|
|
'sso_id' => $ssoUser->id,
|
|
|
|
'sso_provider' => $ssoUser->provider,
|
2024-03-30 16:49:25 +00:00
|
|
|
]);
|
|
|
|
|
|
|
|
return $existingUser;
|
|
|
|
}
|
|
|
|
|
|
|
|
return $this->createUser(
|
2024-03-31 17:19:03 +00:00
|
|
|
name: $ssoUser->name,
|
|
|
|
email: $ssoUser->email,
|
2024-03-30 16:49:25 +00:00
|
|
|
plainTextPassword: '',
|
|
|
|
isAdmin: false,
|
2024-03-31 17:19:03 +00:00
|
|
|
avatar: $ssoUser->avatar,
|
|
|
|
ssoId: $ssoUser->id,
|
|
|
|
ssoProvider: $ssoUser->provider,
|
2024-03-30 16:49:25 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2024-03-19 22:48:12 +00:00
|
|
|
public function updateUser(
|
|
|
|
User $user,
|
|
|
|
string $name,
|
|
|
|
string $email,
|
2024-03-30 16:49:25 +00:00
|
|
|
?string $password = null,
|
2024-03-19 22:48:12 +00:00
|
|
|
?bool $isAdmin = null,
|
|
|
|
?string $avatar = null
|
|
|
|
): User {
|
2023-08-20 22:35:58 +00:00
|
|
|
throw_if($user->is_prospect, new UserProspectUpdateDeniedException());
|
|
|
|
|
2024-03-30 16:49:25 +00:00
|
|
|
if ($user->sso_provider) {
|
|
|
|
// An SSO profile is largely managed by the SSO provider
|
|
|
|
$user->update([
|
|
|
|
'is_admin' => $isAdmin ?? $user->is_admin,
|
|
|
|
'name' => $name,
|
|
|
|
'avatar' => $avatar ? $this->createNewAvatar($avatar, $user) : null,
|
|
|
|
]);
|
|
|
|
} else {
|
|
|
|
$user->update([
|
|
|
|
'name' => $name,
|
|
|
|
'email' => $email,
|
|
|
|
'password' => $password ? $this->hash->make($password) : $user->password,
|
|
|
|
'is_admin' => $isAdmin ?? $user->is_admin,
|
|
|
|
'avatar' => $avatar ? $this->createNewAvatar($avatar, $user) : null,
|
|
|
|
]);
|
2022-07-29 06:47:10 +00:00
|
|
|
}
|
|
|
|
|
2024-03-30 16:49:25 +00:00
|
|
|
return $user;
|
|
|
|
}
|
2024-03-19 22:48:12 +00:00
|
|
|
|
2024-03-30 16:49:25 +00:00
|
|
|
/**
|
|
|
|
* @param string $avatar Either the URL of the avatar or image data
|
|
|
|
*/
|
|
|
|
private function createNewAvatar(string $avatar, ?User $user = null): string
|
|
|
|
{
|
|
|
|
if (Str::startsWith($avatar, ['http://', 'https://'])) {
|
|
|
|
return $avatar;
|
2024-03-19 22:48:12 +00:00
|
|
|
}
|
|
|
|
|
2024-03-30 16:49:25 +00:00
|
|
|
$path = self::generateUserAvatarPath();
|
|
|
|
$this->imageWriter->write($path, $avatar, ['max_width' => 480]);
|
2022-06-10 10:47:46 +00:00
|
|
|
|
2024-03-30 16:49:25 +00:00
|
|
|
optional($user?->getRawOriginal('avatar'), static fn (string $oldAvatar) => File::delete($oldAvatar));
|
|
|
|
|
|
|
|
return basename($path);
|
2022-06-10 10:47:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public function deleteUser(User $user): void
|
|
|
|
{
|
|
|
|
$user->delete();
|
|
|
|
}
|
2024-01-23 22:50:50 +00:00
|
|
|
|
|
|
|
public function savePreference(User $user, string $key, mixed $value): void
|
|
|
|
{
|
|
|
|
$user->preferences = $user->preferences->set($key, $value);
|
|
|
|
|
|
|
|
$user->save();
|
|
|
|
}
|
2024-03-19 22:48:12 +00:00
|
|
|
|
|
|
|
private static function generateUserAvatarPath(): string
|
|
|
|
{
|
|
|
|
return user_avatar_path(sprintf('%s.webp', sha1(Str::uuid())));
|
|
|
|
}
|
2022-06-10 10:47:46 +00:00
|
|
|
}
|