diff --git a/..env.swp b/..env.swp new file mode 100644 index 0000000..61ad66a Binary files /dev/null and b/..env.swp differ diff --git a/.env.example b/.env.example index a3e03b9..42df9d6 100755 --- a/.env.example +++ b/.env.example @@ -11,6 +11,10 @@ APP_SITEHOMEPAGE="" # Void if env is production. NONPROD_FORCE_SECURE=false +# Disables certain features for security purposes while running an open authentication system +# Enable only for demonostration purposes +DEMO_MODE=false + LOG_CHANNEL=daily DB_CONNECTION=mysql diff --git a/app/ApiKey.php b/app/ApiKey.php index 6a97f85..0ed33ff 100644 --- a/app/ApiKey.php +++ b/app/ApiKey.php @@ -20,6 +20,6 @@ class ApiKey extends Model public function user() { - return $this->belongsTo('App\User', 'id'); + return $this->belongsTo('App\User', 'owner_user_id', 'id'); } } diff --git a/app/CustomFacades/IP.php b/app/CustomFacades/IP.php index 230ec83..6753e29 100755 --- a/app/CustomFacades/IP.php +++ b/app/CustomFacades/IP.php @@ -38,13 +38,18 @@ class IP 'ip' => $IP, ]; - // TODO: Maybe unwrap this? Methods are chained here - return json_decode(Cache::remember($IP, 3600, function () use ($IP) { - return Http::get(config('general.urls.ipapi.ipcheck'), [ - 'apiKey' => config('general.keys.ipapi.apikey'), - 'ip' => $IP, - ])->body(); - })); + if (!config('demo.is_enabled')) { + return json_decode(Cache::remember($IP, 3600, function () use ($IP) { + return Http::get(config('general.urls.ipapi.ipcheck'), [ + 'apiKey' => config('general.keys.ipapi.apikey'), + 'ip' => $IP, + ])->body(); + })); + } + + return new class { + public $message = "This feature is disabled."; + }; } } diff --git a/app/Http/Controllers/ApplicationController.php b/app/Http/Controllers/ApplicationController.php index e137503..fcb6dbf 100755 --- a/app/Http/Controllers/ApplicationController.php +++ b/app/Http/Controllers/ApplicationController.php @@ -22,6 +22,7 @@ namespace App\Http\Controllers; use App\Application; +use App\Exceptions\ApplicationNotFoundException; use App\Exceptions\IncompleteApplicationException; use App\Exceptions\UnavailableApplicationException; use App\Exceptions\VacancyNotFoundException; @@ -74,14 +75,22 @@ class ApplicationController extends Controller { $this->authorize('viewAny', Application::class); - return view('dashboard.appmanagement.all'); + return view('dashboard.appmanagement.all') + ->with('applications', Application::all()); } public function renderApplicationForm($vacancySlug) { - return $this->applicationService->renderForm($vacancySlug); + try { + return $this->applicationService->renderForm($vacancySlug); + } + catch (ApplicationNotFoundException $ex) { + return redirect() + ->back() + ->with('error', $ex->getMessage()); + } } public function saveApplicationAnswers(Request $request, $vacancySlug) @@ -98,7 +107,7 @@ class ApplicationController extends Controller } return redirect() - ->back() + ->to(route('showUserApps')) ->with('success', __('Thank you! Your application has been processed and our team will get to it shortly.')); } diff --git a/app/Http/Controllers/Auth/RegisterController.php b/app/Http/Controllers/Auth/RegisterController.php index 41af9f2..3140223 100755 --- a/app/Http/Controllers/Auth/RegisterController.php +++ b/app/Http/Controllers/Auth/RegisterController.php @@ -92,7 +92,7 @@ class RegisterController extends Controller case 'low': $password = ['required', 'string', 'min:10', 'confirmed']; break; - + case 'medium': $password = ['required', 'string', 'confirmed', 'regex:/^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[#?!@$%^&*-]).{12,}$/']; break; @@ -124,11 +124,11 @@ class RegisterController extends Controller 'name' => $data['name'], 'email' => $data['email'], 'password' => Hash::make($data['password']), - 'originalIP' => request()->ip(), + 'originalIP' => config('demo.is_enabled') ? '0.0.0.0' : request()->ip(), ]); // It's not the registration controller's concern to create a profile for the user, - // so this code has been moved to it's respective observer, following the separation of concerns pattern. + // so this code has been moved to its respective observer, following the separation of concerns pattern. $user->assignRole('user'); diff --git a/app/Http/Controllers/BanController.php b/app/Http/Controllers/BanController.php index 3818388..97075d3 100755 --- a/app/Http/Controllers/BanController.php +++ b/app/Http/Controllers/BanController.php @@ -42,6 +42,12 @@ class BanController extends Controller public function insert(BanUserRequest $request, User $user) { + if (config('demo.is_enabled')) { + return redirect() + ->back() + ->with('error', 'This feature is disabled'); + } + $this->authorize('create', [Ban::class, $user]); @@ -60,6 +66,12 @@ class BanController extends Controller public function delete(Request $request, User $user) { + if (config('demo.is_enabled')) { + return redirect() + ->back() + ->with('error', 'This feature is disabled'); + } + $this->authorize('delete', $user->bans); if ($this->suspensionService->isSuspended($user)) { diff --git a/app/Http/Controllers/DashboardController.php b/app/Http/Controllers/DashboardController.php index df46bd2..cb946e1 100755 --- a/app/Http/Controllers/DashboardController.php +++ b/app/Http/Controllers/DashboardController.php @@ -24,6 +24,7 @@ namespace App\Http\Controllers; use App\Application; use App\User; use App\Vacancy; +use Illuminate\Support\Facades\Auth; class DashboardController extends Controller { @@ -34,14 +35,27 @@ class DashboardController extends Controller $totalPeerReview = Application::where('applicationStatus', 'STAGE_PEERAPPROVAL')->get()->count(); $totalNewApplications = Application::where('applicationStatus', 'STAGE_SUBMITTED')->get()->count(); $totalDenied = Application::where('applicationStatus', 'DENIED')->get()->count(); + $vacancies = Vacancy::where('vacancyStatus', '<>', 'CLOSED')->get(); + + $totalDeniedSingle = Application::where([ + ['applicationStatus', '=', 'DENIED'], + ['applicantUserID', '=', Auth::user()->id] + ])->get(); + + $totalNewSingle = Application::where([ + ['applicationStatus', '=', 'STAGE_SUBMITTED'], + ['applicantUserID', '=', Auth::user()->id] + ])->get(); return view('dashboard.dashboard') ->with([ - 'vacancies' => Vacancy::all(), + 'vacancies' => $vacancies, 'totalUserCount' => User::all()->count(), 'totalDenied' => $totalDenied, 'totalPeerReview' => $totalPeerReview, 'totalNewApplications' => $totalNewApplications, + 'totalNewSingle' => $totalNewSingle->count(), + 'totalDeniedSingle' => $totalDeniedSingle->count() ]); } } diff --git a/app/Http/Controllers/FormController.php b/app/Http/Controllers/FormController.php index 82848fe..366475f 100755 --- a/app/Http/Controllers/FormController.php +++ b/app/Http/Controllers/FormController.php @@ -21,6 +21,7 @@ namespace App\Http\Controllers; +use App\Exceptions\EmptyFormException; use App\Exceptions\FormHasConstraintsException; use App\Form; use App\Services\FormManagementService; @@ -53,7 +54,15 @@ class FormController extends Controller public function saveForm(Request $request) { - $form = $this->formService->addForm($request->all()); + try { + $form = $this->formService->addForm($request->all()); + } + catch (EmptyFormException $ex) + { + return redirect() + ->back() + ->with('exception', $ex->getMessage()); + } // Form is boolean or array if ($form) diff --git a/app/Http/Controllers/TeamFileController.php b/app/Http/Controllers/TeamFileController.php index b6fbdc6..30c5994 100755 --- a/app/Http/Controllers/TeamFileController.php +++ b/app/Http/Controllers/TeamFileController.php @@ -62,6 +62,13 @@ class TeamFileController extends Controller { $this->authorize('store', TeamFile::class); + if (config('demo.is_enabled')) + { + return redirect() + ->back() + ->with('error', 'This feature is disabled'); + } + try { $caption = $request->caption; $description = $request->description; @@ -110,6 +117,13 @@ class TeamFileController extends Controller { $this->authorize('delete', $teamFile); + if (config('demo.is_enabled')) + { + return redirect() + ->back() + ->with('error', 'This feature is disabled'); + } + try { Storage::delete($teamFile->fs_location); diff --git a/app/Http/Controllers/UserController.php b/app/Http/Controllers/UserController.php index 0bba54a..2f0d2f4 100755 --- a/app/Http/Controllers/UserController.php +++ b/app/Http/Controllers/UserController.php @@ -32,6 +32,7 @@ use App\Http\Requests\SearchPlayerRequest; use App\Http\Requests\UpdateUserRequest; use App\Notifications\ChangedPassword; use App\Notifications\EmailChanged; +use App\Traits\DisablesFeatures; use App\Traits\ReceivesAccountTokens; use App\User; use Google2FA; @@ -168,6 +169,11 @@ class UserController extends Controller public function changePassword(ChangePasswordRequest $request) { + if (config('demo.is_enabled')) { + return redirect() + ->back() + ->with('error', 'This feature is disabled'); + } $user = User::find(Auth::user()->id); if (! is_null($user)) { @@ -191,6 +197,12 @@ class UserController extends Controller public function changeEmail(ChangeEmailRequest $request) { + if (config('demo.is_enabled')) { + return redirect() + ->back() + ->with('error', 'This feature is disabled'); + } + $user = User::find(Auth::user()->id); if (! is_null($user)) { @@ -214,6 +226,12 @@ class UserController extends Controller public function delete(DeleteUserRequest $request, User $user) { + if (config('demo.is_enabled')) { + return redirect() + ->back() + ->with('error', 'This feature is disabled'); + } + $this->authorize('delete', $user); if ($request->confirmPrompt == 'DELETE ACCOUNT') { @@ -228,6 +246,11 @@ class UserController extends Controller public function update(UpdateUserRequest $request, User $user) { + if (config('demo.is_enabled')) { + return redirect() + ->back() + ->with('error', 'This feature is disabled'); + } $this->authorize('adminEdit', $user); // Mass update would not be possible here without extra code, making route model binding useless @@ -262,6 +285,12 @@ class UserController extends Controller public function add2FASecret(Add2FASecretRequest $request) { + if (config('demo.is_enabled')) { + return redirect() + ->back() + ->with('error', 'This feature is disabled'); + } + $currentSecret = $request->session()->get('current2FA'); $isValid = Google2FA::verifyKey($currentSecret, $request->otp); @@ -314,6 +343,11 @@ class UserController extends Controller public function terminate(Request $request, User $user) { $this->authorize('terminate', User::class); + if (config('demo.is_enabled')) { + return redirect() + ->back() + ->with('error', 'This feature is disabled'); + } // TODO: move logic to policy if (! $user->isStaffMember() || $user->is(Auth::user())) { diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 4b35693..2deea95 100755 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -27,6 +27,7 @@ use App\Observers\UserObserver; use App\User; use Illuminate\Pagination\Paginator; use Illuminate\Support\Facades\Schema; +use Illuminate\Support\Facades\View; use Illuminate\Support\ServiceProvider; use Sentry; @@ -67,5 +68,7 @@ class AppServiceProvider extends ServiceProvider $https = true; $this->app['request']->server->set('HTTPS', $https); + + View::share('demoActive', config('demo.is_enabled')); } } diff --git a/app/Services/ApplicationService.php b/app/Services/ApplicationService.php index f7defa6..a98da16 100644 --- a/app/Services/ApplicationService.php +++ b/app/Services/ApplicationService.php @@ -47,7 +47,7 @@ class ApplicationService * @throws VacancyNotFoundException Thrown when the associated vacancy is not found * @throws IncompleteApplicationException Thrown when there are missing fields */ - public function fillForm(Authenticatable $applicant, array $formData, $vacancySlug): bool + public function fillForm(User $applicant, array $formData, $vacancySlug): bool { $vacancy = Vacancy::with('forms')->where('vacancySlug', $vacancySlug)->get(); diff --git a/app/Services/AppointmentService.php b/app/Services/AppointmentService.php index 35f6de3..c00c666 100644 --- a/app/Services/AppointmentService.php +++ b/app/Services/AppointmentService.php @@ -56,12 +56,7 @@ class AppointmentService */ public function updateAppointment(Application $application, $status, $updateApplication = true) { - $validStatuses = [ - 'SCHEDULED', - 'CONCLUDED', - ]; - - if ($status == 'SCHEDULED' || $status == 'CONCLUDED') + if ($status == 'SCHEDULED' || $status == 'concluded') { $application->appointment->appointmentStatus = strtoupper($status); $application->appointment->save(); diff --git a/app/Services/DemoService.php b/app/Services/DemoService.php new file mode 100644 index 0000000..969de1d --- /dev/null +++ b/app/Services/DemoService.php @@ -0,0 +1,11 @@ +back() + ->with('error', 'This feature is disabled'); + } + // a little verbose $user = User::find(Auth::user()->id); $tokens = $user->generateAccountTokens(); @@ -49,6 +56,13 @@ trait ReceivesAccountTokens public function processDeleteConfirmation(Request $request, $ID, $action, $token) { + if (config('demo.is_enabled')) + { + return redirect() + ->back() + ->with('error', 'This feature is disabled'); + } + // We can't rely on Laravel's route model injection, because it'll ignore soft-deleted models, // so we have to use a special scope to find them ourselves. $user = User::withTrashed()->findOrFail($ID); diff --git a/config/demo.php b/config/demo.php new file mode 100644 index 0000000..a9824c3 --- /dev/null +++ b/config/demo.php @@ -0,0 +1,7 @@ + env('DEMO_MODE', false) + +]; diff --git a/database/seeders/UserSeeder.php b/database/seeders/UserSeeder.php index 4c2ed3a..05963d1 100755 --- a/database/seeders/UserSeeder.php +++ b/database/seeders/UserSeeder.php @@ -35,32 +35,34 @@ class UserSeeder extends Seeder */ public function run() { - + /** * Rationale: * A ghost account is an account used by deleted users. * Essentially, when users are deleted, their content is re-assigned to the * ghost account. - * Also used by one-off apps. - * + * Also used by one-off apps. + * * The ghost account was inspired by Github's ghost account. */ $ghostAccount = User::create([ - 'uuid' => '069a79f444e94726a5befca90e38aaf5', // Notch + 'uuid' => 'b741345057274a519144881927be0290', // Ghost 'name' => 'Ghost (deleted account)', - 'email' => 'blackhole@spacejewel-hosting.com', + 'email' => 'blackhole@example.com', + 'email_verified_at' => now(), 'username' => 'ghost', 'originalIP' => '0.0.0.0', - 'password' => 'locked' + 'password' => 'locked' ])->assignRole('user'); // There can't be role-less users $admin = User::create([ - 'uuid' => '6102256abd284dd7b68e4c96ef313734', + 'uuid' => '069a79f444e94726a5befca90e38aaf5', // Notch 'name' => 'Admin', 'email' => 'admin@example.com', + 'email_verified_at' => now(), 'username' => 'admin', - 'originalIP' => '217.1.189.34', + 'originalIP' => '0.0.0.0', 'password' => Hash::make('password'), ])->assignRole([ // all privileges @@ -68,7 +70,33 @@ class UserSeeder extends Seeder 'reviewer', 'admin', 'hiringManager', - 'developer' + ]); + + $staffmember = User::create([ + 'uuid' => '853c80ef3c3749fdaa49938b674adae6', // Jeb__ + 'name' => 'Staff Member', + 'email' => 'staffmember@example.com', + 'email_verified_at' => now(), + 'username' => 'staffmember', + 'originalIP' => '0.0.0.0', + 'password' => Hash::make('password'), + + ])->assignRole([ // all privileges + 'user', + 'reviewer', + ]); + + $user = User::create([ + 'uuid' => 'f7c77d999f154a66a87dc4a51ef30d19', // hypixel + 'name' => 'End User', + 'email' => 'enduser@example.com', + 'email_verified_at' => now(), + 'username' => 'enduser', + 'originalIP' => '0.0.0.0', + 'password' => Hash::make('password'), + + ])->assignRole([ // all privileges + 'user', ]); } diff --git a/resources/views/auth/login.blade.php b/resources/views/auth/login.blade.php index 2b31cf0..aa66135 100755 --- a/resources/views/auth/login.blade.php +++ b/resources/views/auth/login.blade.php @@ -16,6 +16,22 @@
{{__('messages.signin_cta')}}