Add new location and user management via CLI

This commit is contained in:
Dane Everitt 2017-09-15 22:13:33 -05:00
parent a498bbc7d5
commit 542d1f8db7
No known key found for this signature in database
GPG key ID: EEA66103B3D71F53
10 changed files with 395 additions and 33 deletions

View file

@ -0,0 +1,97 @@
<?php
/*
* Pterodactyl - Panel
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>.
*
* 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\Console\Commands\Location;
use Illuminate\Console\Command;
use Pterodactyl\Services\Locations\LocationDeletionService;
use Pterodactyl\Contracts\Repository\LocationRepositoryInterface;
class DeleteLocationCommand extends Command
{
/**
* @var \Pterodactyl\Services\Locations\LocationDeletionService
*/
protected $deletionService;
/**
* @var \Illuminate\Support\Collection
*/
protected $locations;
/**
* @var \Pterodactyl\Contracts\Repository\LocationRepositoryInterface
*/
protected $repository;
/**
* @var string
*/
protected $signature = 'p:location:delete
{--minimal : Passing this flag will hide the list of current locations.}
{--short= : The short code of the location to delete.}';
/**
* DeleteLocationCommand constructor.
*
* @param \Pterodactyl\Contracts\Repository\LocationRepositoryInterface $repository
* @param \Pterodactyl\Services\Locations\LocationDeletionService $deletionService
*/
public function __construct(
LocationDeletionService $deletionService,
LocationRepositoryInterface $repository
) {
parent::__construct();
$this->deletionService = $deletionService;
$this->repository = $repository;
}
/**
* Respond to the command request.
*
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
* @throws \Pterodactyl\Exceptions\Service\Location\HasActiveNodesException
*/
public function handle()
{
$this->locations = $this->locations ?? $this->repository->getAllWithNodes();
$short = $this->option('short') ?? $this->anticipate(
trans('command/messages.location.ask_short'), $this->locations->pluck('short')->toArray()
);
$location = $this->locations->where('short', $short)->first();
if (is_null($location)) {
$this->error(trans('command/messages.location.no_location_found'));
if (is_null($this->option('short')) && ! $this->option('no-interaction')) {
$this->handle();
}
return;
}
$this->line(trans('command/messages.location.deleted'));
$this->deletionService->handle($location->id);
}
}

View file

@ -22,51 +22,56 @@
* SOFTWARE. * SOFTWARE.
*/ */
namespace Pterodactyl\Console\Commands; namespace Pterodactyl\Console\Commands\Location;
use Illuminate\Console\Command; use Illuminate\Console\Command;
use Pterodactyl\Services\Locations\LocationCreationService;
class AddLocation extends Command class MakeLocationCommand extends Command
{ {
protected $data = []; /**
* @var \Pterodactyl\Services\Locations\LocationCreationService
*/
protected $creationService;
/** /**
* The name and signature of the console command.
*
* @var string * @var string
*/ */
protected $signature = 'pterodactyl:location protected $signature = 'p:location:make
{--short= : The shortcode name of this location (ex. us1).} {--short= : The shortcode name of this location (ex. us1).}
{--long= : A longer description of this location.}'; {--long= : A longer description of this location.}';
/** /**
* The console command description.
*
* @var string * @var string
*/ */
protected $description = 'Creates a new location on the system via the CLI.'; protected $description = 'Creates a new location on the system via the CLI.';
/** /**
* Create a new command instance. * Create a new command instance.
*
* @param \Pterodactyl\Services\Locations\LocationCreationService $creationService
*/ */
public function __construct() public function __construct(LocationCreationService $creationService)
{ {
parent::__construct(); parent::__construct();
$this->creationService = $creationService;
} }
/** /**
* Execute the console command. * Handle the command execution process.
* *
* @return mixed * @throws \Pterodactyl\Exceptions\Model\DataValidationException
*/ */
public function handle() public function handle()
{ {
$this->data['short'] = (is_null($this->option('short'))) ? $this->ask('Location Short Code') : $this->option('short'); $short = $this->option('short') ?? $this->ask(trans('command/messages.location.ask_short'));
$this->data['long'] = (is_null($this->option('long'))) ? $this->ask('Location Description') : $this->option('long'); $long = $this->option('long') ?? $this->ask(trans('command/messages.location.ask_long'));
$repo = new LocationRepository; $location = $this->creationService->handle(compact('short', 'long'));
$id = $repo->create($this->data); $this->line(trans('command/messages.location.created', [
'name' => $location->short,
$this->info('Location ' . $this->data['short'] . ' created with ID: ' . $id); 'id' => $location->id,
]));
} }
} }

View file

@ -0,0 +1,108 @@
<?php
/*
* Pterodactyl - Panel
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>.
*
* 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\Console\Commands\User;
use Webmozart\Assert\Assert;
use Illuminate\Console\Command;
use Pterodactyl\Services\Users\UserDeletionService;
use Pterodactyl\Contracts\Repository\UserRepositoryInterface;
class DeleteUserCommand extends Command
{
/**
* @var \Pterodactyl\Services\Users\UserDeletionService
*/
protected $deletionService;
/**
* @var string
*/
protected $description = 'Deletes a user from the Panel if no servers are attached to their account.';
/**
* @var \Pterodactyl\Contracts\Repository\UserRepositoryInterface
*/
protected $repository;
/**
* @var string
*/
protected $signature = 'p:user:delete {--user=}';
public function __construct(
UserDeletionService $deletionService,
UserRepositoryInterface $repository
) {
parent::__construct();
$this->deletionService = $deletionService;
$this->repository = $repository;
}
/**
* @return bool
* @throws \Pterodactyl\Exceptions\DisplayException
*/
public function handle()
{
$search = $this->option('user') ?? $this->ask(trans('command/messages.user.search_users'));
Assert::notEmpty($search, 'Search term must be a non-null value, received %s.');
$results = $this->repository->search($search)->all();
if (count($results) < 1) {
$this->error(trans('command/messages.user.no_users_found'));
if (! $this->option('no-interaction')) {
return $this->handle();
}
return false;
}
if (! $this->option('no-interaction')) {
$tableValues = [];
foreach ($results as $user) {
$tableValues[] = [$user->id, $user->email, $user->name];
}
$this->table(['User ID', 'Email', 'Name'], $tableValues);
if (! $deleteUser = $this->ask(trans('command/messages.user.select_search_user'))) {
return $this->handle();
}
} else {
if (count($results) > 1) {
$this->error(trans('command/messages.user.multiple_found'));
return false;
}
$deleteUser = $results->first();
}
if ($this->confirm(trans('command/messages.user.confirm_delete')) || $this->option('no-interaction')) {
$this->deletionService->handle($deleteUser);
$this->info(trans('command/messages.user.deleted'));
}
}
}

View file

@ -0,0 +1,78 @@
<?php
/*
* Pterodactyl - Panel
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>.
*
* 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\Console\Commands\User;
use Illuminate\Console\Command;
use Pterodactyl\Services\Users\UserCreationService;
class MakeUserCommand extends Command
{
/**
* @var \Pterodactyl\Services\Users\UserCreationService
*/
protected $creationService;
protected $signature = 'p:user:make {--email=} {--username=} {--name-first=} {--name-last=} {--password=} {--no-password}';
/**
* MakeUserCommand constructor.
*
* @param \Pterodactyl\Services\Users\UserCreationService $creationService
*/
public function __construct(UserCreationService $creationService)
{
parent::__construct();
$this->creationService = $creationService;
}
/**
* Handle command request to create a new user.
*
* @throws \Exception
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
*/
public function handle()
{
$email = $this->option('email') ?? $this->ask(trans('command/messages.user.ask_email'));
$username = $this->option('username') ?? $this->ask(trans('command/messages.user.ask_username'));
$name_first = $this->option('name-first') ?? $this->ask(trans('command/messages.user.ask_name_first'));
$name_last = $this->option('name-last') ?? $this->ask(trans('command/messages.user.ask_name_last'));
if (is_null($password = $this->option('password')) && ! $this->option('no-password')) {
$this->warn(trans('command/messages.user.ask_password_help'));
$this->line(trans('command/messages.user.ask_password_tip'));
$password = $this->secret(trans('command/messages.user.ask_password'));
}
$user = $this->creationService->handle(compact('email', 'username', 'name_first', 'name_last', 'password'));
$this->table(['Field', 'Value'], [
['UUID', $user->uuid],
['Email', $user->email],
['Username', $user->username],
['Name', $user->name],
]);
}
}

View file

@ -3,7 +3,11 @@
namespace Pterodactyl\Console; namespace Pterodactyl\Console;
use Illuminate\Console\Scheduling\Schedule; use Illuminate\Console\Scheduling\Schedule;
use Pterodactyl\Console\Commands\User\MakeUserCommand;
use Pterodactyl\Console\Commands\User\DeleteUserCommand;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel; use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
use Pterodactyl\Console\Commands\Location\MakeLocationCommand;
use Pterodactyl\Console\Commands\Location\DeleteLocationCommand;
class Kernel extends ConsoleKernel class Kernel extends ConsoleKernel
{ {
@ -13,17 +17,21 @@ class Kernel extends ConsoleKernel
* @var array * @var array
*/ */
protected $commands = [ protected $commands = [
\Pterodactyl\Console\Commands\MakeUser::class, DeleteLocationCommand::class,
\Pterodactyl\Console\Commands\ShowVersion::class, DeleteUserCommand::class,
\Pterodactyl\Console\Commands\UpdateEnvironment::class, MakeLocationCommand::class,
\Pterodactyl\Console\Commands\RunTasks::class, MakeUserCommand::class,
\Pterodactyl\Console\Commands\ClearTasks::class, // \Pterodactyl\Console\Commands\MakeUser::class,
\Pterodactyl\Console\Commands\ClearServices::class, // \Pterodactyl\Console\Commands\ShowVersion::class,
\Pterodactyl\Console\Commands\UpdateEmailSettings::class, // \Pterodactyl\Console\Commands\UpdateEnvironment::class,
\Pterodactyl\Console\Commands\CleanServiceBackup::class, // \Pterodactyl\Console\Commands\RunTasks::class,
\Pterodactyl\Console\Commands\AddNode::class, // \Pterodactyl\Console\Commands\ClearTasks::class,
\Pterodactyl\Console\Commands\AddLocation::class, // \Pterodactyl\Console\Commands\ClearServices::class,
\Pterodactyl\Console\Commands\RebuildServer::class, // \Pterodactyl\Console\Commands\UpdateEmailSettings::class,
// \Pterodactyl\Console\Commands\CleanServiceBackup::class,
// \Pterodactyl\Console\Commands\AddNode::class,
// \Pterodactyl\Console\Commands\MakeLocationCommand::class,
// \Pterodactyl\Console\Commands\RebuildServer::class,
]; ];
/** /**

View file

@ -120,6 +120,8 @@ interface RepositoryInterface
* *
* @param array $fields * @param array $fields
* @return mixed * @return mixed
*
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
*/ */
public function findFirstWhere(array $fields); public function findFirstWhere(array $fields);

View file

@ -300,6 +300,16 @@ class User extends Model implements
$this->attributes['username'] = strtolower($value); $this->attributes['username'] = strtolower($value);
} }
/**
* Return a concated result for the accounts full name.
*
* @return string
*/
public function getNameAttribute()
{
return $this->name_first . ' ' . $this->name_last;
}
/** /**
* Returns all permissions that a user has. * Returns all permissions that a user has.
* *

View file

@ -30,6 +30,7 @@ use Illuminate\Database\Query\Expression;
use Pterodactyl\Contracts\Repository\RepositoryInterface; use Pterodactyl\Contracts\Repository\RepositoryInterface;
use Pterodactyl\Exceptions\Model\DataValidationException; use Pterodactyl\Exceptions\Model\DataValidationException;
use Pterodactyl\Exceptions\Repository\RecordNotFoundException; use Pterodactyl\Exceptions\Repository\RecordNotFoundException;
use Pterodactyl\Contracts\Repository\Attributes\SearchableInterface;
abstract class EloquentRepository extends Repository implements RepositoryInterface abstract class EloquentRepository extends Repository implements RepositoryInterface
{ {
@ -106,7 +107,7 @@ abstract class EloquentRepository extends Repository implements RepositoryInterf
$instance = $this->getBuilder()->where($fields)->first($this->getColumns()); $instance = $this->getBuilder()->where($fields)->first($this->getColumns());
if (! $instance) { if (! $instance) {
throw new RecordNotFoundException(); throw new RecordNotFoundException;
} }
return $instance; return $instance;
@ -200,7 +201,12 @@ abstract class EloquentRepository extends Repository implements RepositoryInterf
*/ */
public function all() public function all()
{ {
return $this->getBuilder()->get($this->getColumns()); $instance = $this->getBuilder();
if (interface_exists(SearchableInterface::class)) {
$instance = $instance->search($this->searchTerm);
}
return $instance->get($this->getColumns());
} }
/** /**

View file

@ -24,7 +24,7 @@
return [ return [
'exceptions' => [ 'exceptions' => [
'user_has_servers' => 'Cannot delete a user with active servers attached to their account. Please delete their server\'s before continuing.', 'user_has_servers' => 'Cannot delete a user with active servers attached to their account. Please delete their servers before continuing.',
], ],
'notices' => [ 'notices' => [
'account_created' => 'Account has been created successfully.', 'account_created' => 'Account has been created successfully.',

View file

@ -0,0 +1,48 @@
<?php
/*
* Pterodactyl - Panel
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>.
*
* 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.
*/
return [
'location' => [
'no_location_found' => 'Could not locate a record matching the provided short code.',
'ask_short' => 'Location Short Code',
'ask_long' => 'Location Description',
'created' => 'Successfully created a new location (:name) with an ID of :id.',
'deleted' => 'Successfully deleted the requested location.',
],
'user' => [
'search_users' => 'Enter a Username, UUID, or Email Address',
'select_search_user' => 'ID of user to delete (Enter \'0\' to re-search)',
'deleted' => 'User successfully deleted from the Panel.',
'confirm_delete' => 'Are you sure you want to delete this user from the Panel?',
'no_users_found' => 'No users were found for the search term provided.',
'multiple_found' => 'Multiple accounts were found for the user provided, unable to delete a user because of the --no-interaction flag.',
'ask_email' => 'Email Address',
'ask_username' => 'Username',
'ask_name_first' => 'First Name',
'ask_name_last' => 'Last Name',
'ask_password' => 'Password',
'ask_password_tip' => 'If you would like to create an account with a random password emailed to the user, re-run this command (CTRL+C) and pass the `--no-password` flag.',
'ask_password_help' => 'Passwords must be at least 8 characters in length and contain at least one capital letter and number.',
],
];