From 5c2b9deb096491528a01946b03bec100fa814439 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sat, 10 Jun 2017 22:28:44 -0500 Subject: [PATCH] Push initial implementations of new repository structure This breaks almost the entire panel, do not pull this branch in this state. Mostly just moved old repository files to a new folder without updating anything else in order to start doing new things. Structure is not finalized. --- app/Console/Commands/MakeUser.php | 4 +- app/Contracts/Criteria/CriteriaInterface.php | 39 +++ .../Repositories/RepositoryInterface.php | 164 +++++++++++++ .../SearchableRepositoryInterface.php | 36 +++ app/Contracts/Repositories/UserInterface.php | 30 +++ app/Exceptions/DisplayException.php | 2 +- app/Exceptions/DisplayValidationException.php | 2 +- app/Exceptions/PterodactylException.php | 30 +++ .../Repository/RepositoryException.php | 30 +++ .../Controllers/API/Admin/UserController.php | 8 +- .../Controllers/Admin/OptionController.php | 36 ++- app/Http/Controllers/Admin/UserController.php | 112 ++++----- .../Controllers/Base/AccountController.php | 4 +- app/Http/Requests/Admin/AdminFormRequest.php | 59 +++++ .../Admin/Service/EditOptionScript.php | 46 ++++ .../Admin/Service/StoreOptionVariable.php | 32 +-- app/Http/Requests/Admin/UserFormRequest.php | 79 ++++++ app/Models/User.php | 18 +- app/Observers/UserObserver.php | 15 +- app/Providers/RepositoryServiceProvider.php | 40 +++ app/Repositories/Eloquent/UserRepository.php | 78 ++++++ app/Repositories/{ => Old}/APIRepository.php | 0 .../{ => Old}/DatabaseRepository.php | 0 .../{ => Old}/HelperRepository.php | 0 .../{ => Old}/LocationRepository.php | 0 app/Repositories/{ => Old}/NodeRepository.php | 0 .../{ => Old}/OptionRepository.php | 94 ++++--- app/Repositories/{ => Old}/PackRepository.php | 0 .../{ => Old}/ServiceRepository.php | 0 .../{ => Old}/SubuserRepository.php | 2 +- app/Repositories/{ => Old}/TaskRepository.php | 0 .../{ => Old}/VariableRepository.php | 0 .../old_ServerRepository.php} | 2 +- .../old_UserRepository.php} | 2 +- app/Repositories/Repository.php | 229 ++++++++++++++++++ config/app.php | 1 + ..._06_10_152951_add_external_id_to_users.php | 32 +++ .../pterodactyl/admin/users/view.blade.php | 1 + routes/admin.php | 22 +- 39 files changed, 1083 insertions(+), 166 deletions(-) create mode 100644 app/Contracts/Criteria/CriteriaInterface.php create mode 100644 app/Contracts/Repositories/RepositoryInterface.php create mode 100644 app/Contracts/Repositories/SearchableRepositoryInterface.php create mode 100644 app/Contracts/Repositories/UserInterface.php create mode 100644 app/Exceptions/PterodactylException.php create mode 100644 app/Exceptions/Repository/RepositoryException.php create mode 100644 app/Http/Requests/Admin/AdminFormRequest.php create mode 100644 app/Http/Requests/Admin/Service/EditOptionScript.php create mode 100644 app/Http/Requests/Admin/UserFormRequest.php create mode 100644 app/Providers/RepositoryServiceProvider.php create mode 100644 app/Repositories/Eloquent/UserRepository.php rename app/Repositories/{ => Old}/APIRepository.php (100%) rename app/Repositories/{ => Old}/DatabaseRepository.php (100%) rename app/Repositories/{ => Old}/HelperRepository.php (100%) rename app/Repositories/{ => Old}/LocationRepository.php (100%) rename app/Repositories/{ => Old}/NodeRepository.php (100%) rename app/Repositories/{ => Old}/OptionRepository.php (79%) rename app/Repositories/{ => Old}/PackRepository.php (100%) rename app/Repositories/{ => Old}/ServiceRepository.php (100%) rename app/Repositories/{ => Old}/SubuserRepository.php (99%) rename app/Repositories/{ => Old}/TaskRepository.php (100%) rename app/Repositories/{ => Old}/VariableRepository.php (100%) rename app/Repositories/{ServerRepository.php => Old/old_ServerRepository.php} (99%) rename app/Repositories/{UserRepository.php => Old/old_UserRepository.php} (99%) create mode 100644 app/Repositories/Repository.php create mode 100644 database/migrations/2017_06_10_152951_add_external_id_to_users.php diff --git a/app/Console/Commands/MakeUser.php b/app/Console/Commands/MakeUser.php index 05803dd3..81038cb4 100644 --- a/app/Console/Commands/MakeUser.php +++ b/app/Console/Commands/MakeUser.php @@ -25,7 +25,7 @@ namespace Pterodactyl\Console\Commands; use Illuminate\Console\Command; -use Pterodactyl\Repositories\UserRepository; +use Pterodactyl\Repositories\oldUserRepository; class MakeUser extends Command { @@ -80,7 +80,7 @@ class MakeUser extends Command $data['root_admin'] = is_null($this->option('admin')) ? $this->confirm('Is this user a root administrator?') : $this->option('admin'); try { - $user = new UserRepository; + $user = new oldUserRepository; $user->create($data); return $this->info('User successfully created.'); diff --git a/app/Contracts/Criteria/CriteriaInterface.php b/app/Contracts/Criteria/CriteriaInterface.php new file mode 100644 index 00000000..dba7a688 --- /dev/null +++ b/app/Contracts/Criteria/CriteriaInterface.php @@ -0,0 +1,39 @@ +. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +namespace Pterodactyl\Contracts\Criteria; + +use Pterodactyl\Repositories\Repository; + +interface CriteriaInterface +{ + /** + * Apply selected criteria to a repository call. + * + * @param \Illuminate\Database\Eloquent\Model $model + * @param \Pterodactyl\Repositories\Repository $repository + * @return mixed + */ + public function apply($model, Repository $repository); +} diff --git a/app/Contracts/Repositories/RepositoryInterface.php b/app/Contracts/Repositories/RepositoryInterface.php new file mode 100644 index 00000000..16771e70 --- /dev/null +++ b/app/Contracts/Repositories/RepositoryInterface.php @@ -0,0 +1,164 @@ +. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +namespace Pterodactyl\Contracts\Repositories; + +use Illuminate\Container\Container; + +interface RepositoryInterface +{ + /** + * RepositoryInterface constructor. + * + * @param \Illuminate\Container\Container $container + */ + public function __construct(Container $container); + + /** + * Define the model class to be loaded. + * + * @return string + */ + public function model(); + + /** + * Returns the raw model class. + * + * @return \Illuminate\Database\Eloquent\Model + */ + public function getModel(); + + /** + * Make the model instance. + * + * @return \Illuminate\Database\Eloquent\Model + * @throws \Pterodactyl\Exceptions\Repository\RepositoryException + */ + public function makeModel(); + + /** + * Return all of the currently defined rules. + * + * @return array + */ + public function getRules(); + + /** + * Return the rules to apply when updating a model. + * + * @return array + */ + public function getUpdateRules(); + + /** + * Return the rules to apply when creating a model. + * + * @return array + */ + public function getCreateRules(); + + /** + * Add relations to a model for retrieval. + * + * @param array $params + * @return $this + */ + public function with(...$params); + + /** + * Add count of related items to model when retrieving. + * + * @param array $params + * @return $this + */ + public function withCount(...$params); + + /** + * Get all records from the database. + * + * @param array $columns + * @return mixed + */ + public function all(array $columns = ['*']); + + /** + * Return a paginated result set. + * + * @param int $limit + * @param array $columns + * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator + */ + public function paginate($limit = 15, array $columns = ['*']); + + /** + * Create a new record on the model. + * + * @param array $data + * @return \Illuminate\Database\Eloquent\Model + */ + public function create(array $data); + + /** + * Update the model. + * + * @param $attributes + * @param array $data + * @return int + */ + public function update($attributes, array $data); + + /** + * Delete a model from the database. Handles soft deletion. + * + * @param int $id + * @return mixed + */ + public function delete($id); + + /** + * Destroy the model from the database. Ignores soft deletion. + * + * @param int $id + * @return mixed + */ + public function destroy($id); + + /** + * Find a given model by ID or IDs. + * + * @param int|array $id + * @param array $columns + * @return \Illuminate\Database\Eloquent\Model|\Illuminate\Database\Eloquent\Collection + */ + public function find($id, array $columns = ['*']); + + /** + * Finds the first record matching a passed array of values. + * + * @param array $attributes + * @param array $columns + * @return \Illuminate\Database\Eloquent\Model + */ + public function findBy(array $attributes, array $columns = ['*']); +} diff --git a/app/Contracts/Repositories/SearchableRepositoryInterface.php b/app/Contracts/Repositories/SearchableRepositoryInterface.php new file mode 100644 index 00000000..6a6b4537 --- /dev/null +++ b/app/Contracts/Repositories/SearchableRepositoryInterface.php @@ -0,0 +1,36 @@ +. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +namespace Pterodactyl\Contracts\Repositories; + +interface SearchableRepositoryInterface extends RepositoryInterface +{ + /** + * Pass parameters to search trait on model. + * + * @param string $term + * @return mixed + */ + public function search($term); +} diff --git a/app/Contracts/Repositories/UserInterface.php b/app/Contracts/Repositories/UserInterface.php new file mode 100644 index 00000000..a7bf4964 --- /dev/null +++ b/app/Contracts/Repositories/UserInterface.php @@ -0,0 +1,30 @@ +. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +namespace Pterodactyl\Contracts\Repositories; + +interface UserInterface extends RepositoryInterface, SearchableRepositoryInterface +{ + // +} diff --git a/app/Exceptions/DisplayException.php b/app/Exceptions/DisplayException.php index f3f8fd71..530ad40c 100644 --- a/app/Exceptions/DisplayException.php +++ b/app/Exceptions/DisplayException.php @@ -26,7 +26,7 @@ namespace Pterodactyl\Exceptions; use Log; -class DisplayException extends \Exception +class DisplayException extends PterodactylException { /** * Exception constructor. diff --git a/app/Exceptions/DisplayValidationException.php b/app/Exceptions/DisplayValidationException.php index 3d8a4fda..ae97318a 100644 --- a/app/Exceptions/DisplayValidationException.php +++ b/app/Exceptions/DisplayValidationException.php @@ -24,7 +24,7 @@ namespace Pterodactyl\Exceptions; -class DisplayValidationException extends \Exception +class DisplayValidationException extends PterodactylException { // } diff --git a/app/Exceptions/PterodactylException.php b/app/Exceptions/PterodactylException.php new file mode 100644 index 00000000..4ec48d66 --- /dev/null +++ b/app/Exceptions/PterodactylException.php @@ -0,0 +1,30 @@ +. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +namespace Pterodactyl\Exceptions; + +class PterodactylException extends \Exception +{ + // +} diff --git a/app/Exceptions/Repository/RepositoryException.php b/app/Exceptions/Repository/RepositoryException.php new file mode 100644 index 00000000..b55b6304 --- /dev/null +++ b/app/Exceptions/Repository/RepositoryException.php @@ -0,0 +1,30 @@ +. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +namespace Pterodactyl\Exceptions\Repository; + +class RepositoryException extends \Exception +{ + // +} diff --git a/app/Http/Controllers/API/Admin/UserController.php b/app/Http/Controllers/API/Admin/UserController.php index c94fe809..b524d1ee 100644 --- a/app/Http/Controllers/API/Admin/UserController.php +++ b/app/Http/Controllers/API/Admin/UserController.php @@ -30,7 +30,7 @@ use Illuminate\Http\Request; use Pterodactyl\Models\User; use Pterodactyl\Exceptions\DisplayException; use Pterodactyl\Http\Controllers\Controller; -use Pterodactyl\Repositories\UserRepository; +use Pterodactyl\Repositories\oldUserRepository; use Pterodactyl\Transformers\Admin\UserTransformer; use Pterodactyl\Exceptions\DisplayValidationException; use League\Fractal\Pagination\IlluminatePaginatorAdapter; @@ -91,7 +91,7 @@ class UserController extends Controller { $this->authorize('user-create', $request->apiKey()); - $repo = new UserRepository; + $repo = new oldUserRepository; try { $user = $repo->create($request->only([ 'custom_id', 'email', 'password', 'name_first', @@ -128,7 +128,7 @@ class UserController extends Controller { $this->authorize('user-edit', $request->apiKey()); - $repo = new UserRepository; + $repo = new oldUserRepository; try { $user = $repo->update($user, $request->intersect([ 'email', 'password', 'name_first', @@ -165,7 +165,7 @@ class UserController extends Controller { $this->authorize('user-delete', $request->apiKey()); - $repo = new UserRepository; + $repo = new oldUserRepository; try { $repo->delete($id); diff --git a/app/Http/Controllers/Admin/OptionController.php b/app/Http/Controllers/Admin/OptionController.php index cdcf2f3b..afabcfd2 100644 --- a/app/Http/Controllers/Admin/OptionController.php +++ b/app/Http/Controllers/Admin/OptionController.php @@ -26,8 +26,10 @@ namespace Pterodactyl\Http\Controllers\Admin; use Log; use Alert; +use Route; use Javascript; use Illuminate\Http\Request; +use Pterodactyl\Http\Requests\Admin\Service\EditOptionScript; use Pterodactyl\Models\Service; use Pterodactyl\Models\ServiceOption; use Pterodactyl\Exceptions\DisplayException; @@ -39,6 +41,21 @@ use Pterodactyl\Http\Requests\Admin\Service\StoreOptionVariable; class OptionController extends Controller { + /** + * Store the repository instance. + * + * @var \Pterodactyl\Repositories\OptionRepository + */ + protected $repository; + + /** + * OptionController constructor. + */ + public function __construct() + { + $this->repository = new OptionRepository(Route::current()->parameter('option')); + } + /** * Handles request to view page for adding new option. * @@ -227,24 +244,17 @@ class OptionController extends Controller } /** - * Handles POST when updating scripts for a service option. + * Handles POST when updating script for a service option. * - * @param Request $request - * @param int $id - * @return \Illuminate\Response\RedirectResponse + * @param \Pterodactyl\Http\Requests\Admin\Service\EditOptionScript $request + * @return \Illuminate\Http\RedirectResponse */ - public function updateScripts(Request $request, $id) + public function updateScripts(EditOptionScript $request) { - $repo = new OptionRepository; - try { - $repo->scripts($id, $request->only([ - 'script_install', 'script_entry', - 'script_container', 'copy_script_from', - ])); + $this->repository->scripts($request->normalize()); + Alert::success('Successfully updated option scripts to be run when servers are installed.')->flash(); - } catch (DisplayValidationException $ex) { - return redirect()->route('admin.services.option.scripts', $id)->withErrors(json_decode($ex->getMessage())); } catch (DisplayException $ex) { Alert::danger($ex->getMessage())->flash(); } catch (\Exception $ex) { diff --git a/app/Http/Controllers/Admin/UserController.php b/app/Http/Controllers/Admin/UserController.php index 0e943eb4..1b963228 100644 --- a/app/Http/Controllers/Admin/UserController.php +++ b/app/Http/Controllers/Admin/UserController.php @@ -25,17 +25,31 @@ namespace Pterodactyl\Http\Controllers\Admin; -use Log; use Alert; use Illuminate\Http\Request; -use Pterodactyl\Models\User; +use Pterodactyl\Contracts\Repositories\UserInterface; use Pterodactyl\Exceptions\DisplayException; +use Pterodactyl\Http\Requests\Admin\UserFormRequest; +use Pterodactyl\Models\User; use Pterodactyl\Http\Controllers\Controller; -use Pterodactyl\Repositories\UserRepository; -use Pterodactyl\Exceptions\DisplayValidationException; class UserController extends Controller { + /** + * @var \Pterodactyl\Repositories\Eloquent\UserRepository + */ + protected $repository; + + /** + * UserController constructor. + * + * @param \Pterodactyl\Contracts\Repositories\UserInterface $repository + */ + public function __construct(UserInterface $repository) + { + $this->repository = $repository; + } + /** * Display user index page. * @@ -44,7 +58,7 @@ class UserController extends Controller */ public function index(Request $request) { - $users = User::withCount('servers', 'subuserOf'); + $users = $this->repository->withCount('servers', 'subuserOf'); if (! is_null($request->input('query'))) { $users->search($request->input('query')); @@ -58,10 +72,9 @@ class UserController extends Controller /** * Display new user page. * - * @param \Illuminate\Http\Request $request * @return \Illuminate\View\View */ - public function create(Request $request) + public function create() { return view('admin.users.new'); } @@ -69,96 +82,61 @@ class UserController extends Controller /** * Display user view page. * - * @param \Illuminate\Http\Request $request - * @param int $id + * @param \Pterodactyl\Models\User $user * @return \Illuminate\View\View */ - public function view(Request $request, $id) + public function view(User $user) { return view('admin.users.view', [ - 'user' => User::with('servers.node')->findOrFail($id), + 'user' => $user, ]); } /** - * Delete a user. + * Delete a user from the system. * - * @param \Illuminate\Http\Request $request - * @param int $id + * @param \Pterodactyl\Models\User $user * @return \Illuminate\Http\RedirectResponse */ - public function delete(Request $request, $id) + public function delete(User $user) { try { - $repo = new UserRepository; - $repo->delete($id); - Alert::success('Successfully deleted user from system.')->flash(); + $this->repository->delete($user->id); return redirect()->route('admin.users'); } catch (DisplayException $ex) { Alert::danger($ex->getMessage())->flash(); - } catch (\Exception $ex) { - Log::error($ex); - Alert::danger('An exception was encountered while attempting to delete this user.')->flash(); } - return redirect()->route('admin.users.view', $id); + return redirect()->route('admin.users.view', $user->id); } /** * Create a user. * - * @param \Illuminate\Http\Request $request + * @param \Pterodactyl\Http\Requests\Admin\UserFormRequest $request * @return \Illuminate\Http\RedirectResponse */ - public function store(Request $request) + public function store(UserFormRequest $request) { - try { - $user = new UserRepository; - $userid = $user->create($request->only([ - 'email', 'password', 'name_first', - 'name_last', 'username', 'root_admin', - ])); - Alert::success('Account has been successfully created.')->flash(); + $user = $this->repository->create($request->normalize()); + Alert::success('Account has been successfully created.')->flash(); - return redirect()->route('admin.users.view', $userid); - } catch (DisplayValidationException $ex) { - return redirect()->route('admin.users.new')->withErrors(json_decode($ex->getMessage()))->withInput(); - } catch (\Exception $ex) { - Log::error($ex); - Alert::danger('An error occured while attempting to add a new user.')->flash(); - - return redirect()->route('admin.users.new'); - } + return redirect()->route('admin.users.view', $user->id); } /** - * Update a user. + * Update a user on the system. * - * @param \Illuminate\Http\Request $request - * @param int $id + * @param \Pterodactyl\Http\Requests\Admin\UserFormRequest $request + * @param \Pterodactyl\Models\User $user * @return \Illuminate\Http\RedirectResponse */ - public function update(Request $request, $id) + public function update(UserFormRequest $request, User $user) { - try { - $repo = new UserRepository; - $user = $repo->update($id, array_merge( - $request->only('root_admin'), - $request->intersect([ - 'email', 'password', 'name_first', - 'name_last', 'username', - ]) - )); - Alert::success('User account was successfully updated.')->flash(); - } catch (DisplayValidationException $ex) { - return redirect()->route('admin.users.view', $id)->withErrors(json_decode($ex->getMessage())); - } catch (\Exception $ex) { - Log::error($ex); - Alert::danger('An error occured while attempting to update this user.')->flash(); - } + $this->repository->update($user->id, $request->normalize()); - return redirect()->route('admin.users.view', $id); + return redirect()->route('admin.users.view', $user->id); } /** @@ -169,12 +147,12 @@ class UserController extends Controller */ public function json(Request $request) { - return User::select('id', 'email', 'username', 'name_first', 'name_last') - ->search($request->input('q')) - ->get()->transform(function ($item) { - $item->md5 = md5(strtolower($item->email)); + return $this->repository->search($request->input('q'))->all([ + 'id', 'email', 'username', 'name_first', 'name_last', + ])->transform(function ($item) { + $item->md5 = md5(strtolower($item->email)); - return $item; - }); + return $item; + }); } } diff --git a/app/Http/Controllers/Base/AccountController.php b/app/Http/Controllers/Base/AccountController.php index 10c33e38..7163045a 100644 --- a/app/Http/Controllers/Base/AccountController.php +++ b/app/Http/Controllers/Base/AccountController.php @@ -30,7 +30,7 @@ use Alert; use Illuminate\Http\Request; use Pterodactyl\Models\User; use Pterodactyl\Http\Controllers\Controller; -use Pterodactyl\Repositories\UserRepository; +use Pterodactyl\Repositories\oldUserRepository; use Pterodactyl\Exceptions\DisplayValidationException; class AccountController extends Controller @@ -90,7 +90,7 @@ class AccountController extends Controller } try { - $repo = new UserRepository; + $repo = new oldUserRepository; $repo->update($request->user()->id, $data); Alert::success('Your account details were successfully updated.')->flash(); } catch (DisplayValidationException $ex) { diff --git a/app/Http/Requests/Admin/AdminFormRequest.php b/app/Http/Requests/Admin/AdminFormRequest.php new file mode 100644 index 00000000..e3e0be37 --- /dev/null +++ b/app/Http/Requests/Admin/AdminFormRequest.php @@ -0,0 +1,59 @@ +. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +namespace Pterodactyl\Http\Requests\Admin; + +use Pterodactyl\Models\User; +use Illuminate\Foundation\Http\FormRequest; + +abstract class AdminFormRequest extends FormRequest +{ + /** + * Determine if the user is an admin and has permission to access this + * form controller in the first place. + * + * @return bool + */ + public function authorize() + { + if (! $this->user() instanceof User) { + return false; + } + + return $this->user()->isRootAdmin(); + } + + /** + * Return only the fields that we are interested in from the request. + * This will include empty fields as a null value. + * + * @return array + */ + public function normalize() + { + return $this->only( + array_keys($this->rules()) + ); + } +} diff --git a/app/Http/Requests/Admin/Service/EditOptionScript.php b/app/Http/Requests/Admin/Service/EditOptionScript.php new file mode 100644 index 00000000..52cfff4e --- /dev/null +++ b/app/Http/Requests/Admin/Service/EditOptionScript.php @@ -0,0 +1,46 @@ +. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +namespace Pterodactyl\Http\Requests\Admin\Service; + +use Pterodactyl\Http\Requests\Admin\AdminFormRequest; + +class EditOptionScript extends AdminFormRequest +{ + /** + * Return the rules to be used when validating the sent data in the request. + * + * @return array + */ + public function rules() + { + return [ + 'script_install' => 'sometimes|nullable|string', + 'script_is_privileged' => 'sometimes|required|boolean', + 'script_entry' => 'sometimes|required|string', + 'script_container' => 'sometimes|required|string', + 'copy_script_from' => 'sometimes|nullable|numeric', + ]; + } +} diff --git a/app/Http/Requests/Admin/Service/StoreOptionVariable.php b/app/Http/Requests/Admin/Service/StoreOptionVariable.php index cb37e6a1..869b2efc 100644 --- a/app/Http/Requests/Admin/Service/StoreOptionVariable.php +++ b/app/Http/Requests/Admin/Service/StoreOptionVariable.php @@ -24,25 +24,10 @@ namespace Pterodactyl\Http\Requests\Admin\Service; -use Pterodactyl\Models\User; -use Illuminate\Foundation\Http\FormRequest; +use Pterodactyl\Http\Requests\Admin\AdminFormRequest; -class StoreOptionVariable extends FormRequest +class StoreOptionVariable extends AdminFormRequest { - /** - * Determine if user is allowed to access this request. - * - * @return bool - */ - public function authorize() - { - if (! $this->user() instanceof User) { - return false; - } - - return $this->user()->isRootAdmin(); - } - /** * Set the rules to be used for data passed to the request. * @@ -59,17 +44,4 @@ class StoreOptionVariable extends FormRequest 'options' => 'sometimes|required|array', ]; } - - /** - * Return only the fields that we are interested in from the request. - * This will include empty fields as a null value. - * - * @return array - */ - public function normalize() - { - return $this->only( - array_keys($this->rules()) - ); - } } diff --git a/app/Http/Requests/Admin/UserFormRequest.php b/app/Http/Requests/Admin/UserFormRequest.php new file mode 100644 index 00000000..10d55977 --- /dev/null +++ b/app/Http/Requests/Admin/UserFormRequest.php @@ -0,0 +1,79 @@ +. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +namespace Pterodactyl\Http\Requests\Admin; + +use Pterodactyl\Models\User; +use Illuminate\Support\Facades\Hash; +use Pterodactyl\Contracts\Repositories\UserInterface; + +class UserFormRequest extends AdminFormRequest +{ + /** + * {@inheritdoc} + */ + public function repository() + { + return UserInterface::class; + } + + /** + * {@inheritdoc} + */ + public function rules() + { + if ($this->method() === 'PATCH') { + return [ + 'email' => 'sometimes|required|email|unique:users,email,' . $this->user->id, + 'username' => 'sometimes|required|alpha_dash|between:1,255|unique:users,username, ' . $this->user->id . '|' . User::USERNAME_RULES, + 'name_first' => 'sometimes|required|string|between:1,255', + 'name_last' => 'sometimes|required|string|between:1,255', + 'password' => 'sometimes|nullable|' . User::PASSWORD_RULES, + 'root_admin' => 'sometimes|required|boolean', + 'language' => 'sometimes|required|string|min:1|max:5', + 'use_totp' => 'sometimes|required|boolean', + 'totp_secret' => 'sometimes|required|size:16', + ]; + } + + return [ + 'email' => 'required|email|unique:users,email,' . $this->user->id, + 'username' => 'required|alpha_dash|between:1,255|unique:users,username,' . $this->user->id . '|' . User::USERNAME_RULES, + 'name_first' => 'required|string|between:1,255', + 'name_last' => 'required|string|between:1,255', + 'password' => 'sometimes|nullable|' . User::PASSWORD_RULES, + 'root_admin' => 'required|boolean', + 'external_id' => 'sometimes|nullable|numeric|unique:users,external_id', + ]; + } + + public function normalize() + { + if ($this->has('password')) { + $this->merge(['password' => Hash::make($this->input('password'))]); + } + + return parent::normalize(); + } +} diff --git a/app/Models/User.php b/app/Models/User.php index 12504b6b..a4f06f4b 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -117,6 +117,7 @@ class User extends Model implements AuthenticatableContract, AuthorizableContrac * * @param int $token * @return bool + * @deprecated */ public function toggleTotp($token) { @@ -136,9 +137,11 @@ class User extends Model implements AuthenticatableContract, AuthorizableContrac * - at least one lowercase character * - at least one number. * - * @param string $password - * @param string $regex + * @param string $password + * @param string $regex * @return void + * @throws \Pterodactyl\Exceptions\DisplayException + * @deprecated */ public function setPassword($password, $regex = '((?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,})') { @@ -165,6 +168,7 @@ class User extends Model implements AuthenticatableContract, AuthorizableContrac * Return true or false depending on wether the user is root admin or not. * * @return bool + * @deprecated */ public function isRootAdmin() { @@ -256,6 +260,16 @@ class User extends Model implements AuthenticatableContract, AuthorizableContrac return $query; } + /** + * Store the username as a lowecase string. + * + * @param string $value + */ + public function setUsernameAttribute($value) + { + $this->attributes['username'] = strtolower($value); + } + /** * Returns all permissions that a user has. * diff --git a/app/Observers/UserObserver.php b/app/Observers/UserObserver.php index e7b07851..f6e16026 100644 --- a/app/Observers/UserObserver.php +++ b/app/Observers/UserObserver.php @@ -30,9 +30,17 @@ use Carbon; use Pterodactyl\Events; use Pterodactyl\Models\User; use Pterodactyl\Notifications\AccountCreated; +use Pterodactyl\Services\UuidService; class UserObserver { + protected $uuid; + + public function __construct(UuidService $uuid) + { + $this->uuid = $uuid; + } + /** * Listen to the User creating event. * @@ -41,6 +49,8 @@ class UserObserver */ public function creating(User $user) { + $user->uuid = $this->uuid->generate(); + event(new Events\User\Creating($user)); } @@ -52,8 +62,7 @@ class UserObserver */ public function created(User $user) { - event(new Events\User\Created($user)); - + dd($user); if ($user->password === 'unset') { $token = hash_hmac('sha256', str_random(40), config('app.key')); DB::table('password_resets')->insert([ @@ -68,6 +77,8 @@ class UserObserver 'username' => $user->username, 'token' => (isset($token)) ? $token : null, ])); + + event(new Events\User\Created($user)); } /** diff --git a/app/Providers/RepositoryServiceProvider.php b/app/Providers/RepositoryServiceProvider.php new file mode 100644 index 00000000..745e6d05 --- /dev/null +++ b/app/Providers/RepositoryServiceProvider.php @@ -0,0 +1,40 @@ +. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +namespace Pterodactyl\Providers; + +use Illuminate\Support\ServiceProvider; +use Pterodactyl\Contracts\Repositories\UserInterface; +use Pterodactyl\Repositories\Eloquent\UserRepository; + +class RepositoryServiceProvider extends ServiceProvider +{ + /** + * Register the repositories. + */ + public function register() + { + $this->app->bind(UserInterface::class, UserRepository::class); + } +} diff --git a/app/Repositories/Eloquent/UserRepository.php b/app/Repositories/Eloquent/UserRepository.php new file mode 100644 index 00000000..2ca9c521 --- /dev/null +++ b/app/Repositories/Eloquent/UserRepository.php @@ -0,0 +1,78 @@ +. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +namespace Pterodactyl\Repositories\Eloquent; + +use Pterodactyl\Models\User; +use Illuminate\Contracts\Auth\Guard; +use Pterodactyl\Repositories\Repository; +use Pterodactyl\Exceptions\DisplayException; +use Pterodactyl\Contracts\Repositories\UserInterface; + +class UserRepository extends Repository implements UserInterface +{ + /** + * Dependencies to automatically inject into the repository. + * + * @var array + */ + protected $inject = [ + 'guard' => Guard::class, + ]; + + /** + * Return the model to be used for the repository. + * + * @return string + */ + public function model() + { + return User::class; + } + + /** + * {@inheritdoc} + */ + public function search($term) + { + $this->model->search($term); + + return $this; + } + + public function delete($id) + { + $user = $this->model->withCount('servers')->find($id); + + if ($this->guard->user() && $this->guard->user()->id === $user->id) { + throw new DisplayException('You cannot delete your own account.'); + } + + if ($user->server_count > 0) { + throw new DisplayException('Cannot delete an account that has active servers attached to it.'); + } + + return $user->delete(); + } +} diff --git a/app/Repositories/APIRepository.php b/app/Repositories/Old/APIRepository.php similarity index 100% rename from app/Repositories/APIRepository.php rename to app/Repositories/Old/APIRepository.php diff --git a/app/Repositories/DatabaseRepository.php b/app/Repositories/Old/DatabaseRepository.php similarity index 100% rename from app/Repositories/DatabaseRepository.php rename to app/Repositories/Old/DatabaseRepository.php diff --git a/app/Repositories/HelperRepository.php b/app/Repositories/Old/HelperRepository.php similarity index 100% rename from app/Repositories/HelperRepository.php rename to app/Repositories/Old/HelperRepository.php diff --git a/app/Repositories/LocationRepository.php b/app/Repositories/Old/LocationRepository.php similarity index 100% rename from app/Repositories/LocationRepository.php rename to app/Repositories/Old/LocationRepository.php diff --git a/app/Repositories/NodeRepository.php b/app/Repositories/Old/NodeRepository.php similarity index 100% rename from app/Repositories/NodeRepository.php rename to app/Repositories/Old/NodeRepository.php diff --git a/app/Repositories/OptionRepository.php b/app/Repositories/Old/OptionRepository.php similarity index 79% rename from app/Repositories/OptionRepository.php rename to app/Repositories/Old/OptionRepository.php index 1a0ce450..6e99583c 100644 --- a/app/Repositories/OptionRepository.php +++ b/app/Repositories/Old/OptionRepository.php @@ -26,12 +26,67 @@ namespace Pterodactyl\Repositories; use DB; use Validator; +use InvalidArgumentException; use Pterodactyl\Models\ServiceOption; use Pterodactyl\Exceptions\DisplayException; use Pterodactyl\Exceptions\DisplayValidationException; class OptionRepository { + /** + * Store the requested service option. + * + * @var \Pterodactyl\Models\ServiceOption + */ + protected $model; + + /** + * OptionRepository constructor. + * + * @param null|int|\Pterodactyl\Models\ServiceOption $option + */ + public function __construct($option = null) + { + if (is_null($option)) { + return; + } + + if ($option instanceof ServiceOption) { + $this->model = $option; + } else { + if (! is_numeric($option)) { + throw new InvalidArgumentException( + sprintf('Variable passed to constructor must be integer or instance of \Pterodactyl\Models\ServiceOption.') + ); + } + + $this->model = ServiceOption::findOrFail($option); + } + } + + /** + * Return the eloquent model for the given repository. + * + * @return null|\Pterodactyl\Models\ServiceOption + */ + public function getModel() + { + return $this->model; + } + + /** + * Update the currently assigned model by re-initalizing the class. + * + * @param null|int|\Pterodactyl\Models\ServiceOption $option + * @return $this + */ + public function setModel($option) + { + self::__construct($option); + + return $this; + } + /** * Creates a new service option on the system. * @@ -67,7 +122,7 @@ class OptionRepository } } - return ServiceOption::create($data); + return $this->setModel(ServiceOption::create($data))->getModel(); } /** @@ -76,13 +131,15 @@ class OptionRepository * @param int $id * @return void * + * @throws \Exception * @throws \Pterodactyl\Exceptions\DisplayException + * @throws \Throwable */ public function delete($id) { - $option = ServiceOption::with('variables')->withCount('servers')->findOrFail($id); + $this->model->load('variables', 'servers'); - if ($option->servers_count > 0) { + if ($this->model->servers->count() > 0) { throw new DisplayException('You cannot delete a service option that has servers associated with it.'); } @@ -158,32 +215,19 @@ class OptionRepository /** * Updates a service option's scripts in the database. * - * @param int $id * @param array $data - * @return \Pterodactyl\Models\ServiceOption * * @throws \Pterodactyl\Exceptions\DisplayException - * @throws \Pterodactyl\Exceptions\DisplayValidationException */ - public function scripts($id, array $data) + public function scripts(array $data) { - $option = ServiceOption::findOrFail($id); - $data['script_install'] = empty($data['script_install']) ? null : $data['script_install']; - $validator = Validator::make($data, [ - 'script_install' => 'sometimes|nullable|string', - 'script_is_privileged' => 'sometimes|required|boolean', - 'script_entry' => 'sometimes|required|string', - 'script_container' => 'sometimes|required|string', - 'copy_script_from' => 'sometimes|nullable|numeric', - ]); - if (isset($data['copy_script_from']) && ! empty($data['copy_script_from'])) { - $select = ServiceOption::whereNull('copy_script_from')->where([ - ['id', $data['copy_script_from']], - ['service_id', $option->service_id], - ])->first(); + $select = ServiceOption::whereNull('copy_script_from') + ->where('id', $data['copy_script_from']) + ->where('service_id', $this->model->service_id) + ->first(); if (! $select) { throw new DisplayException('The service option selected to copy a script from either does not exist, or is copying from a higher level.'); @@ -192,12 +236,6 @@ class OptionRepository $data['copy_script_from'] = null; } - if ($validator->fails()) { - throw new DisplayValidationException(json_encode($validator->errors())); - } - - $option->fill($data)->save(); - - return $option; + $this->model->fill($data)->save(); } } diff --git a/app/Repositories/PackRepository.php b/app/Repositories/Old/PackRepository.php similarity index 100% rename from app/Repositories/PackRepository.php rename to app/Repositories/Old/PackRepository.php diff --git a/app/Repositories/ServiceRepository.php b/app/Repositories/Old/ServiceRepository.php similarity index 100% rename from app/Repositories/ServiceRepository.php rename to app/Repositories/Old/ServiceRepository.php diff --git a/app/Repositories/SubuserRepository.php b/app/Repositories/Old/SubuserRepository.php similarity index 99% rename from app/Repositories/SubuserRepository.php rename to app/Repositories/Old/SubuserRepository.php index 7a2aef8c..b7d73655 100644 --- a/app/Repositories/SubuserRepository.php +++ b/app/Repositories/Old/SubuserRepository.php @@ -79,7 +79,7 @@ class SubuserRepository $user = User::where('email', $data['email'])->first(); if (! $user) { try { - $repo = new UserRepository; + $repo = new oldUserRepository; $user = $repo->create([ 'email' => $data['email'], 'username' => str_random(8), diff --git a/app/Repositories/TaskRepository.php b/app/Repositories/Old/TaskRepository.php similarity index 100% rename from app/Repositories/TaskRepository.php rename to app/Repositories/Old/TaskRepository.php diff --git a/app/Repositories/VariableRepository.php b/app/Repositories/Old/VariableRepository.php similarity index 100% rename from app/Repositories/VariableRepository.php rename to app/Repositories/Old/VariableRepository.php diff --git a/app/Repositories/ServerRepository.php b/app/Repositories/Old/old_ServerRepository.php similarity index 99% rename from app/Repositories/ServerRepository.php rename to app/Repositories/Old/old_ServerRepository.php index c13613bf..e7547404 100644 --- a/app/Repositories/ServerRepository.php +++ b/app/Repositories/Old/old_ServerRepository.php @@ -43,7 +43,7 @@ use Pterodactyl\Services\DeploymentService; use Pterodactyl\Exceptions\DisplayException; use Pterodactyl\Exceptions\DisplayValidationException; -class ServerRepository +class old_ServerRepository { /** * An array of daemon permission to assign to this server. diff --git a/app/Repositories/UserRepository.php b/app/Repositories/Old/old_UserRepository.php similarity index 99% rename from app/Repositories/UserRepository.php rename to app/Repositories/Old/old_UserRepository.php index 05a1a53c..06538a4d 100644 --- a/app/Repositories/UserRepository.php +++ b/app/Repositories/Old/old_UserRepository.php @@ -35,7 +35,7 @@ use Pterodactyl\Services\UuidService; use Pterodactyl\Exceptions\DisplayException; use Pterodactyl\Exceptions\DisplayValidationException; -class UserRepository +class old_UserRepository { /** * Creates a user on the panel. Returns the created user's ID. diff --git a/app/Repositories/Repository.php b/app/Repositories/Repository.php new file mode 100644 index 00000000..37e2c421 --- /dev/null +++ b/app/Repositories/Repository.php @@ -0,0 +1,229 @@ +. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +namespace Pterodactyl\Repositories; + +use Illuminate\Container\Container; +use Illuminate\Database\Eloquent\Model; +use Pterodactyl\Contracts\Repositories\RepositoryInterface; +use Pterodactyl\Exceptions\Repository\RepositoryException; + +abstract class Repository implements RepositoryInterface +{ + const RULE_UPDATED = 'updated'; + const RULE_CREATED = 'created'; + + /** + * @var \Illuminate\Container\Container + */ + protected $container; + + /** + * Array of classes to inject automatically into the container. + * + * @var array + */ + protected $inject = []; + + /** + * @var \Illuminate\Database\Eloquent\Model + */ + protected $model; + + /** + * Array of validation rules that can be accessed from this repository. + * + * @var array + */ + protected $rules = []; + + /** + * {@inheritdoc} + */ + public function __construct(Container $container) + { + $this->container = $container; + + foreach ($this->inject as $key => $value) { + if (isset($this->{$key})) { + throw new \Exception('Cannot override a defined object in this class.'); + } + + $this->{$key} = $this->container->make($value); + } + + $this->makeModel(); + } + + /** + * {@inheritdoc} + */ + abstract public function model(); + + /** + * {@inheritdoc} + */ + public function getModel() + { + return $this->model; + } + + /** + * {@inheritdoc} + */ + public function makeModel() + { + $model = $this->container->make($this->model()); + + if (! $model instanceof Model) { + throw new RepositoryException( + "Class {$this->model()} must be an instance of \\Illuminate\\Database\\Eloquent\\Model" + ); + } + + return $this->model = $model->newQuery(); + } + + /** + * {@inheritdoc} + */ + public function getRules() + { + return $this->rules; + } + + /** + * {@inheritdoc} + */ + public function getUpdateRules() + { + if (array_key_exists(self::RULE_UPDATED, $this->rules)) { + return $this->rules[self::RULE_UPDATED]; + } + + return []; + } + + /** + * {@inheritdoc} + */ + public function getCreateRules() + { + if (array_key_exists(self::RULE_CREATED, $this->rules)) { + return $this->rules[self::RULE_CREATED]; + } + + return []; + } + + /** + * {@inheritdoc} + */ + public function with(...$params) + { + $this->model = $this->model->with($params); + + return $this; + } + + /** + * {@inheritdoc} + */ + public function withCount(...$params) + { + $this->model = $this->model->withCount($params); + + return $this; + } + + /** + * {@inheritdoc} + */ + public function all(array $columns = ['*']) + { + return $this->model->get($columns); + } + + /** + * {@inheritdoc} + */ + public function paginate($limit = 15, array $columns = ['*']) + { + return $this->model->paginate($limit, $columns); + } + + /** + * {@inheritdoc} + */ + public function create(array $data) + { + return $this->model->create($data); + } + + /** + * {@inheritdoc} + */ + public function update($attributes, array $data) + { + // If only a number is passed, we assume it is an ID + // for the specific model at hand. + if (is_numeric($attributes)) { + $attributes = [['id', '=', $attributes]]; + } + + return $this->model->where($attributes)->get()->each->update($data); + } + + /** + * {@inheritdoc} + */ + public function delete($id) + { + return $this->model->find($id)->delete(); + } + + /** + * {@inheritdoc} + */ + public function destroy($id) + { + return $this->model->find($id)->forceDelete(); + } + + /** + * {@inheritdoc} + */ + public function find($id, array $columns = ['*']) + { + return $this->model->find($id, $columns); + } + + /** + * {@inheritdoc} + */ + public function findBy(array $attributes, array $columns = ['*']) + { + return $this->model->where($attributes)->first($columns); + } +} diff --git a/config/app.php b/config/app.php index 3c3fb772..0b6dbeeb 100644 --- a/config/app.php +++ b/config/app.php @@ -163,6 +163,7 @@ return [ Pterodactyl\Providers\AppServiceProvider::class, Pterodactyl\Providers\AuthServiceProvider::class, Pterodactyl\Providers\EventServiceProvider::class, + Pterodactyl\Providers\RepositoryServiceProvider::class, Pterodactyl\Providers\RouteServiceProvider::class, Pterodactyl\Providers\MacroServiceProvider::class, Pterodactyl\Providers\PhraseAppTranslationProvider::class, diff --git a/database/migrations/2017_06_10_152951_add_external_id_to_users.php b/database/migrations/2017_06_10_152951_add_external_id_to_users.php new file mode 100644 index 00000000..696f10f4 --- /dev/null +++ b/database/migrations/2017_06_10_152951_add_external_id_to_users.php @@ -0,0 +1,32 @@ +unsignedInteger('external_id')->after('id')->nullable()->unique(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('users', function (Blueprint $table) { + $table->dropColumn('external_id'); + }); + } +} diff --git a/resources/themes/pterodactyl/admin/users/view.blade.php b/resources/themes/pterodactyl/admin/users/view.blade.php index 39461e46..3e23bb26 100644 --- a/resources/themes/pterodactyl/admin/users/view.blade.php +++ b/resources/themes/pterodactyl/admin/users/view.blade.php @@ -68,6 +68,7 @@ diff --git a/routes/admin.php b/routes/admin.php index 7b6c459e..2fe4a0b2 100644 --- a/routes/admin.php +++ b/routes/admin.php @@ -81,12 +81,12 @@ Route::group(['prefix' => 'users'], function () { Route::get('/', 'UserController@index')->name('admin.users'); Route::get('/accounts.json', 'UserController@json')->name('admin.users.json'); Route::get('/new', 'UserController@create')->name('admin.users.new'); - Route::get('/view/{id}', 'UserController@view')->name('admin.users.view'); + Route::get('/view/{user}', 'UserController@view')->name('admin.users.view'); Route::post('/new', 'UserController@store'); - Route::post('/view/{id}', 'UserController@update'); + Route::patch('/view/{user}', 'UserController@update'); - Route::delete('/view/{id}', 'UserController@delete'); + Route::delete('/view/{user}', 'UserController@delete'); }); /* @@ -168,17 +168,17 @@ Route::group(['prefix' => 'services'], function () { Route::get('/view/{id}', 'ServiceController@view')->name('admin.services.view'); Route::get('/view/{id}/functions', 'ServiceController@viewFunctions')->name('admin.services.view.functions'); Route::get('/option/new', 'OptionController@create')->name('admin.services.option.new'); - Route::get('/option/{id}', 'OptionController@viewConfiguration')->name('admin.services.option.view'); - Route::get('/option/{id}/variables', 'OptionController@viewVariables')->name('admin.services.option.variables'); - Route::get('/option/{id}/scripts', 'OptionController@viewScripts')->name('admin.services.option.scripts'); + Route::get('/option/{option}', 'OptionController@viewConfiguration')->name('admin.services.option.view'); + Route::get('/option/{option}/variables', 'OptionController@viewVariables')->name('admin.services.option.variables'); + Route::get('/option/{option}/scripts', 'OptionController@viewScripts')->name('admin.services.option.scripts'); Route::post('/new', 'ServiceController@store'); - Route::post('/view/{id}', 'ServiceController@edit'); + Route::post('/view/{option}', 'ServiceController@edit'); Route::post('/option/new', 'OptionController@store'); - Route::post('/option/{id}', 'OptionController@editConfiguration'); - Route::post('/option/{id}/scripts', 'OptionController@updateScripts'); - Route::post('/option/{id}/variables', 'OptionController@createVariable'); - Route::post('/option/{id}/variables/{variable}', 'OptionController@editVariable')->name('admin.services.option.variables.edit'); + Route::post('/option/{option}', 'OptionController@editConfiguration'); + Route::post('/option/{option}/scripts', 'OptionController@updateScripts'); + Route::post('/option/{option}/variables', 'OptionController@createVariable'); + Route::post('/option/{option}/variables/{variable}', 'OptionController@editVariable')->name('admin.services.option.variables.edit'); Route::delete('/view/{id}', 'ServiceController@delete'); });