first commit
Some checks failed
Build / run (push) Has been cancelled

This commit is contained in:
maher
2025-10-29 11:42:25 +01:00
commit 703f50a09d
4595 changed files with 385164 additions and 0 deletions

View File

@@ -0,0 +1,39 @@
<?php
namespace Common\Auth\Controllers;
use Auth;
use Common\Core\BaseController;
use Illuminate\Http\Request;
class AccessTokenController extends BaseController
{
public function __construct(protected Request $request)
{
$this->middleware(['auth']);
}
public function store()
{
$this->validate($this->request, [
'tokenName' => 'required|string|min:3|max:100',
]);
$token = Auth::user()->createToken($this->request->get('tokenName'));
return $this->success([
'token' => $token->accessToken,
'plainTextToken' => $token->plainTextToken,
]);
}
public function destroy(string $tokenId)
{
Auth::user()
->tokens()
->where('id', $tokenId)
->delete();
return $this->success();
}
}

View File

@@ -0,0 +1,48 @@
<?php
namespace Common\Auth\Controllers;
use App\Models\User;
use Common\Core\BaseController;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Auth;
class BanController extends BaseController
{
public function store(User $user)
{
$this->authorize('destroy', [User::class, [$user->id]]);
if ($user->hasPermission('admin')) {
abort(403, 'Admin users can\'t be suspended');
}
$data = $this->validate(request(), [
'ban_until' => 'nullable|date|after:now',
'comment' => 'nullable|string|max:255',
'permanent' => 'boolean',
]);
$user->bans()->create([
'expired_at' => $data['permanent']
? null
: Arr::get($data, 'ban_until'),
'comment' => Arr::get($data, 'comment'),
'created_by_type' => User::MODEL_TYPE,
'created_by_id' => Auth::id(),
]);
$user->fill(['banned_at' => now()])->save();
return $this->success(['user' => $user]);
}
public function destroy(User $user)
{
$this->authorize('destroy', [User::class, [$user->id]]);
$user->bans()->delete();
$user->fill(['banned_at' => null])->save();
return $this->success(['user' => $user]);
}
}

View File

@@ -0,0 +1,45 @@
<?php namespace Common\Auth\Controllers;
use App\Models\User;
use Auth;
use Common\Core\BaseController;
class EmailVerificationController extends BaseController
{
public function __construct()
{
$this->middleware('auth');
}
public function validateOtp()
{
$code = request('code');
$user = Auth::user();
if (!$code || !$user->emailVerificationOtpIsValid($code)) {
$msg = __(
'The security code you entered is invalid or has expired',
);
return $this->error($msg, [
'code' => $msg,
]);
}
$user->markEmailAsVerified();
return $this->success();
}
public function resendVerificationEmail()
{
$data = $this->validate(request(), ['email' => 'required|email']);
$user = User::where('email', $data['email'])->firstOrFail();
$this->authorize('update', $user);
$user->sendEmailVerificationNotification();
return $this->success();
}
}

View File

@@ -0,0 +1,84 @@
<?php
namespace Common\Auth\Controllers;
use Common\Auth\Fortify\ValidateLoginCredentials;
use Common\Core\BaseController;
use Common\Core\Bootstrap\MobileBootstrapData;
use Illuminate\Auth\Events\Registered;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Laravel\Fortify\Contracts\CreatesNewUsers;
use Laravel\Fortify\Contracts\EmailVerificationNotificationSentResponse;
use Laravel\Fortify\Contracts\RegisterResponse;
use Laravel\Fortify\Fortify;
class MobileAuthController extends BaseController
{
public function login(Request $request)
{
$this->validate($request, [
Fortify::username() => 'required|string',
'password' => 'required|string',
'token_name' => 'required|string|min:3|max:100',
]);
$validator = app(ValidateLoginCredentials::class);
$user = $validator->execute($request);
if (!$user) {
$validator->throwFailedAuthenticationException(
$request,
trans('auth.failed'),
);
}
if (settings('single_device_login')) {
Auth::logoutOtherDevices($request->get('password'));
}
Auth::login($user);
$bootstrapData = app(MobileBootstrapData::class)
->init()
->refreshToken($request->get('token_name'))
->get();
return $this->success($bootstrapData);
}
public function register(
Request $request,
CreatesNewUsers $creator,
): RegisterResponse {
event(new Registered(($user = $creator->create($request->all()))));
Auth::login($user);
return app(RegisterResponse::class);
}
public function sendEmailVerificationNotification()
{
$this->middleware('auth');
if (
request()
->user()
->hasVerifiedEmail()
) {
return request()->wantsJson()
? new JsonResponse('', 204)
: redirect()->intended(
Fortify::redirects('email-verification'),
);
}
request()
->user()
->sendEmailVerificationNotification();
return app(EmailVerificationNotificationSentResponse::class);
}
}

View File

@@ -0,0 +1,166 @@
<?php namespace Common\Auth\Controllers;
use App\Models\User;
use Common\Auth\Oauth;
use Common\Core\BaseController;
use Exception;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Session;
class SocialAuthController extends BaseController
{
public function __construct(protected Oauth $oauth)
{
$this->middleware('auth', [
'only' => ['connect', 'disconnect'],
]);
$this->middleware('guest', [
'only' => ['login'],
]);
}
/**
* Connect specified social account to currently logged-in user.
*/
public function connect(string $provider)
{
if (!settings("social.$provider.enable")) {
abort(403);
}
return $this->oauth->redirect($provider);
}
/**
* Handles case where user is trying to log in with social account whose email
* already exists in database. Request password for local account in that case.
*/
public function connectWithPassword(): JsonResponse
{
// get data for this social login persisted in session
$data = $this->oauth->getPersistedData();
if (!$data) {
return $this->error(__('There was an issue. Please try again.'));
}
if (
!request()->has('password') ||
!Auth::validate([
'email' => $data['profile']->email,
'password' => request('password'),
])
) {
return $this->error(__('Specified credentials are not valid'), [
'password' => __('This password is not correct.'),
]);
}
return $this->success($this->oauth->createUserFromOAuthData($data));
}
public function retrieveProfile(string $providerName)
{
return $this->oauth->retrieveProfileOnly($providerName);
}
/**
* Disconnect specified social account from currently logged-in user.
*/
public function disconnect(string $provider)
{
$this->oauth->disconnect($provider);
return $this->success();
}
/**
* Login with specified social provider.
*/
public function login(string $provider)
{
if (!settings("social.$provider.enable")) {
abort(403);
}
return $this->oauth->loginWith($provider);
}
public function loginCallback(string $provider)
{
if ($handler = Session::get(Oauth::OAUTH_CALLBACK_HANDLER_KEY)) {
return app($handler)->execute($provider);
}
$externalProfile = null;
try {
$externalProfile = $this->oauth->socializeWith(
$provider,
request('tokenFromApi'),
request('secretFromApi'),
);
} catch (Exception $e) {
Log::error($e);
}
if (!$externalProfile) {
return $this->oauth->getErrorResponse(
__('Could not retrieve social sign in account.'),
);
}
// TODO: use new "OAUTH_CALLBACK_HANDLER_KEY" functionality to handle this, remove "tokenFromApi" stuff from this handler
if (Session::get(Oauth::RETRIEVE_PROFILE_ONLY_KEY)) {
Session::forget(Oauth::RETRIEVE_PROFILE_ONLY_KEY);
return $this->oauth->returnProfileData($externalProfile);
}
$existingProfile = $this->oauth->getExistingProfile($externalProfile);
// if user is already logged in, attach returned social account to logged-in user
if (Auth::check()) {
return $this->oauth->attachProfileToExistingUser(
Auth::user(),
$externalProfile,
$provider,
);
}
// if we have already created a user for this social account, log user in
if ($existingProfile?->user) {
$this->oauth->updateSocialProfileData(
$existingProfile,
$provider,
$externalProfile,
);
return $this->oauth->logUserIn($existingProfile->user, $provider);
}
// if user is trying to log in with envato and does not have any valid purchases, bail
if (
$provider === 'envato' &&
empty($externalProfile->user['purchases'])
) {
return $this->oauth->getErrorResponse(
'You do not have any supported purchases.',
);
}
// need to request password from user in order to connect accounts
$user = User::where('email', $externalProfile->email)->first();
if ($user?->password) {
$this->oauth->persistSocialProfileData([
'service' => $provider,
'profile' => $externalProfile,
]);
return $this->oauth->getPopupResponse('REQUEST_PASSWORD');
}
// if we have email and didn't create an account for this profile yet, do it now
return $this->oauth->createUserFromOAuthData([
'profile' => $externalProfile,
'service' => $provider,
]);
}
}

View File

@@ -0,0 +1,17 @@
<?php
namespace Common\Auth\Controllers;
use Common\Core\BaseController;
use Illuminate\Support\Facades\Auth;
class TwoFactorQrCodeController extends BaseController
{
public function show()
{
return $this->success([
'svg' => Auth::user()->twoFactorQrCodeSvg(),
'secret' => decrypt(Auth::user()->two_factor_secret),
]);
}
}

View File

@@ -0,0 +1,62 @@
<?php namespace Common\Auth\Controllers;
use App\Models\User;
use Common\Auth\Events\UserAvatarChanged;
use Common\Core\BaseController;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;
class UserAvatarController extends BaseController
{
public function __construct(
protected Request $request,
protected User $user,
) {
}
public function store(User $user)
{
$this->authorize('update', $user);
$this->validate($this->request, [
'file' => 'required_without:url|image|max:1500',
'url' => 'required_without:file|string|max:250',
]);
// delete old user avatar
if ($user->getRawOriginal('avatar')) {
Storage::disk('public')->delete($user->getRawOriginal('avatar'));
}
// store new avatar on public disk
$path =
$this->request->get('url') ??
$this->request
->file('file')
->storePublicly('avatars', ['disk' => 'public']);
// attach avatar to user model
$user->avatar = $path;
$user->save();
event(new UserAvatarChanged($user));
return $this->success(['user' => $user]);
}
public function destroy(User $user)
{
$this->authorize('update', $user);
if ($user->getRawOriginal('avatar')) {
Storage::disk('public')->delete($user->getRawOriginal('avatar'));
}
$user->avatar = null;
$user->save();
event(new UserAvatarChanged($user));
return $this->success();
}
}

View File

@@ -0,0 +1,105 @@
<?php namespace Common\Auth\Controllers;
use App\Models\User;
use Auth;
use Common\Auth\Actions\CreateUser;
use Common\Auth\Actions\DeleteUsers;
use Common\Auth\Actions\PaginateUsers;
use Common\Auth\Actions\UpdateUser;
use Common\Auth\Requests\CrupdateUserRequest;
use Common\Core\BaseController;
class UserController extends BaseController
{
public function __construct()
{
$this->middleware('auth', ['except' => ['show']]);
}
public function index()
{
$this->authorize('index', User::class);
$pagination = (new PaginateUsers())->execute(request()->all());
return $this->success(['pagination' => $pagination]);
}
public function show(User $user)
{
$relations = array_filter(explode(',', request('with', '')));
$relations = array_merge(['roles', 'social_profiles'], $relations);
if (settings('envato.enable')) {
$relations[] = 'purchase_codes';
}
if (Auth::id() === $user->id) {
$relations[] = 'tokens';
$user->makeVisible([
'two_factor_confirmed_at',
'two_factor_recovery_codes',
]);
if ($user->two_factor_confirmed_at) {
$user->two_factor_recovery_codes = $user->recoveryCodes();
$user->syncOriginal();
}
}
$user->load($relations);
$this->authorize('show', $user);
return $this->success(['user' => $user]);
}
public function store(CrupdateUserRequest $request)
{
$this->authorize('store', User::class);
$user = (new CreateUser())->execute($request->validated());
return $this->success(['user' => $user], 201);
}
public function update(User $user, CrupdateUserRequest $request)
{
$this->authorize('update', $user);
$user = (new UpdateUser())->execute($user, $request->validated());
return $this->success(['user' => $user]);
}
public function destroy(string $ids)
{
$userIds = explode(',', $ids);
$shouldDeleteCurrentUser = request('deleteCurrentUser');
$this->authorize('destroy', [User::class, $userIds]);
$users = User::whereIn('id', $userIds)->get();
// guard against current user or admin user deletion
foreach ($users as $user) {
if (!$shouldDeleteCurrentUser && $user->id === Auth::id()) {
return $this->error(
__('Could not delete currently logged in user: :email', [
'email' => $user->email,
]),
);
}
if ($user->hasPermission('admin')) {
return $this->error(
__('Could not delete admin user: :email', [
'email' => $user->email,
]),
);
}
}
(new DeleteUsers())->execute($users->pluck('id')->toArray());
return $this->success();
}
}

View File

@@ -0,0 +1,30 @@
<?php
namespace Common\Auth\Controllers;
use App\Models\User;
use Common\Core\BaseController;
class UserFollowedUsersController extends BaseController
{
public function index(User $user)
{
$this->authorize('show', $user);
$pagination = $user
->followedUsers()
->withCount(['followers'])
->paginate(request('perPage', 20));
return $this->success(['pagination' => $pagination]);
}
public function ids(User $user)
{
$this->authorize('show', $user);
$ids = $user->followedUsers()->pluck('id');
return $this->success(['ids' => $ids]);
}
}

View File

@@ -0,0 +1,47 @@
<?php namespace Common\Auth\Controllers;
use App\Models\User;
use Auth;
use Common\Core\BaseController;
class UserFollowersController extends BaseController
{
public function __construct()
{
$this->middleware('auth')->except(['index']);
}
public function index(User $user)
{
$this->authorize('show', $user);
$pagination = $user
->followers()
->withCount(['followers'])
->simplePaginate(request('perPage') ?? 20);
return $this->success(['pagination' => $pagination]);
}
public function follow(User $userToFollow)
{
if ($userToFollow->id !== Auth::user()->id) {
Auth::user()
->followedUsers()
->sync([$userToFollow->id], false);
}
return $this->success();
}
public function unfollow(User $userToFollow)
{
if ($userToFollow->id != Auth::user()->id) {
Auth::user()
->followedUsers()
->detach($userToFollow->id);
}
return $this->success();
}
}

View File

@@ -0,0 +1,77 @@
<?php
namespace Common\Auth\Controllers;
use Common\Auth\ActiveSession;
use Common\Core\BaseController;
use Illuminate\Contracts\Auth\StatefulGuard;
use Illuminate\Support\Facades\Auth;
use Jenssegers\Agent\Agent;
class UserSessionsController extends BaseController
{
public function __construct()
{
$this->middleware('auth');
}
public function index()
{
$sessions = Auth::user()
->activeSessions()
->orderBy('updated_at', 'desc')
->limit(30)
->get()
->map(function (ActiveSession $session) {
$agent = new Agent(null, $session->user_agent);
$location = geoip($session->ip_address);
$isCurrentDevice = $session->session_id
? $session->session_id ===
request()
->session()
->getId()
: $session->token ===
Auth::user()->currentAccessToken()->token;
return [
'id' => $session->id,
'platform' => $agent->platform(),
'device_type' => $agent->deviceType(),
'browser' => $agent->browser(),
'country' => $location->country,
'city' => $location->city,
'ip_address' => config('common.site.demo')
? 'Hidden on demo site'
: $session->ip_address,
'is_current_device' => $isCurrentDevice,
'last_active' => $session->updated_at,
];
})
->values();
return $this->success(['sessions' => $sessions]);
}
public function LogoutOtherSessions(StatefulGuard $guard)
{
$data = $this->validate(request(), [
'password' => 'required',
]);
$guard->logoutOtherDevices($data['password']);
ActiveSession::where('user_id', $guard->id())
->whereNotNull('session_id')
->where(
'session_id',
'!=',
request()
->session()
->getId(),
)
->delete();
return $this->success();
}
}