Merge branch 'develop' into feature/vuejs

This commit is contained in:
Dane Everitt 2018-07-02 21:00:42 -07:00
commit 48cb01f438
No known key found for this signature in database
GPG key ID: EEA66103B3D71F53
52 changed files with 666 additions and 529 deletions

View file

@ -1,26 +0,0 @@
<!---
Please take a little time to submit a good issue. It makes our life easier and the issue will be resolved quicker.
!!! GitHub is NOT the place for difficulties setting up this software. Please use it for bugs and feature requests only. If you have issues setting up the panel or the daemon visit our Discord server: https://pterodactyl.io/discord
If you are submitting a feature request please remove everything in here. Then give a detailed explanation what you want to have implemented and why that would be a good addition.
Please also try to give the issue a good title: It should summarize your issue in a few words and help us see what the issue is about in a glance. Things like "Panel is not working" do not help.
--- You can delete everything above this line. --->
<!--- Please fill in the following basic information --->
* Panel or Daemon:
* Version of Panel/Daemon:
* Server's OS:
* Your Computer's OS & Browser:
------------------------
<!---
Please provide as much information about your issue as needed. Include precise steps to reproduce the issue and provide logs of the components that didn't work as expected.
Please provide additional information, depending on what you have issues with:
Panel: `php -v` (the php version in use).
Daemon: `uname -a` and `docker info` (your kernel version and information regarding docker)
--->

27
.github/ISSUE_TEMPLATE/---bug-report.md vendored Normal file
View file

@ -0,0 +1,27 @@
---
name: "\U0001F41B Bug Report"
about: Create a report to help us resolve a bug or error
---
**Background (please complete the following information):**
* Panel or Daemon:
* Version of Panel/Daemon:
* Server's OS:
* Your Computer's OS & Browser:
**Describe the bug**
A clear and concise description of what the bug is.
Please provide additional information too, depending on what you have issues with:
Panel: `php -v` (the php version in use).
Daemon: `uname -a` and `docker info` (your kernel version and information regarding docker)
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen. If applicable, add screenshots or a recording to help explain your problem.

View file

@ -0,0 +1,17 @@
---
name: "\U0001F680 Feature Request"
about: Suggest an idea for this project
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

View file

@ -0,0 +1,8 @@
---
name: "⛔ Installation Help"
about: 'Visit our Discord for installation help: https://pterodactyl.io/discord'
---
We use GitHub issues only to discuss about Pterodactyl bugs and new features. For
this kind of questions about using Pterodactyl, please visit our Discord for assistance: https://pterodactyl.io/discord

View file

@ -1,5 +1,17 @@
language: php language: php
dist: trusty dist: trusty
git:
depth: 3
quiet: true
matrix:
fast_finish: true
allow_failures:
- env: TEST_SUITE=Coverage
env:
matrix:
- TEST_SUITE=Unit
- TEST_SUITE=Coverage
- TEST_SUITE=Integration
php: php:
- 7.2 - 7.2
sudo: false sudo: false
@ -15,8 +27,9 @@ before_script:
- cp .env.travis .env - cp .env.travis .env
- travis_retry composer install --no-interaction --prefer-dist --no-suggest - travis_retry composer install --no-interaction --prefer-dist --no-suggest
script: script:
- vendor/bin/phpunit --bootstrap vendor/autoload.php --coverage-clover coverage.xml tests/Unit - if [ "$TEST_SUITE" = "Unit" ]; then vendor/bin/phpunit --bootstrap vendor/autoload.php tests/Unit; fi;
- vendor/bin/phpunit tests/Integration - if [ "$TEST_SUITE" = "Coverage" ]; then vendor/bin/phpunit --bootstrap vendor/autoload.php --coverage-clover coverage.xml tests/Unit; fi;
- if [ "$TEST_SUITE" = "Integration" ]; then vendor/bin/phpunit tests/Integration; fi;
notifications: notifications:
email: false email: false
webhooks: webhooks:

View file

@ -7,13 +7,22 @@ This project follows [Semantic Versioning](http://semver.org) guidelines.
### Added ### Added
* Nodes can now be put into maintenance mode to deny access to servers temporarily. * Nodes can now be put into maintenance mode to deny access to servers temporarily.
* Basic statistics about your panel are now available in the Admin CP. * Basic statistics about your panel are now available in the Admin CP.
* Added support for using a MySQL socket location for connections rather than a TCP connection. Set a `DB_SOCKET` variable in your `.env` file to use this.
### Fixed ### Fixed
* Hitting Ctrl+Z when editing a file on the web now works as expected. * Hitting Ctrl+Z when editing a file on the web now works as expected.
* Logo now links to the correct location on all pages. * Logo now links to the correct location on all pages.
* Permissions checking to determine if a user can see the task management page now works correctly.
* Fixed `pterodactyl.environment_variables` to be used correctly for global environment variables. The wrong config variable name was being using previously.
* Fixes tokens being sent to users when their account is created to actually work. Implements Laravel's internal token creation mechanisms rather than trying to do it custom.
* Updates some eggs to ensure they have the correct data and will continue working down the road. Fixes autoupdating on some source servers and MC related download links.
* Emails should send properly now when a server is marked as installed to let the owner know it is ready for action.
* Cancelling a file manager operation should cancel correctly across all browsers now.
### Changed ### Changed
* Attempting to upload a folder via the web file manager will now display a warning telling the user to use SFTP. * Attempting to upload a folder via the web file manager will now display a warning telling the user to use SFTP.
* Changing your account password will now log out all other sessions that currently exist for that user.
* Subusers with no permissions selected can be created.
## v0.7.7 (Derelict Dermodactylus) ## v0.7.7 (Derelict Dermodactylus)
### Fixed ### Fixed

View file

@ -0,0 +1,15 @@
<?php
namespace Pterodactyl\Contracts\Core;
use Pterodactyl\Events\Event;
interface ReceivesEvents
{
/**
* Handles receiving an event from the application.
*
* @param \Pterodactyl\Events\Event $notification
*/
public function handle(Event $notification): void;
}

View file

@ -202,7 +202,7 @@ interface RepositoryInterface
public function insertIgnore(array $values): bool; public function insertIgnore(array $values): bool;
/** /**
* Get the amount of entries in the database * Get the amount of entries in the database.
* *
* @return int * @return int
*/ */

View file

@ -147,7 +147,7 @@ interface ServerRepositoryInterface extends RepositoryInterface, SearchableInter
public function isUniqueUuidCombo(string $uuid, string $short): bool; public function isUniqueUuidCombo(string $uuid, string $short): bool;
/** /**
* Get the amount of servers that are suspended * Get the amount of servers that are suspended.
* *
* @return int * @return int
*/ */

View file

@ -0,0 +1,27 @@
<?php
namespace Pterodactyl\Events\Server;
use Pterodactyl\Events\Event;
use Pterodactyl\Models\Server;
use Illuminate\Queue\SerializesModels;
class Installed extends Event
{
use SerializesModels;
/**
* @var \Pterodactyl\Models\Server
*/
public $server;
/**
* Create a new event instance.
*
* @var \Pterodactyl\Models\Server
*/
public function __construct(Server $server)
{
$this->server = $server;
}
}

View file

@ -2,16 +2,14 @@
namespace Pterodactyl\Http\Controllers\Admin; namespace Pterodactyl\Http\Controllers\Admin;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Pterodactyl\Contracts\Repository\AllocationRepositoryInterface;
use Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface;
use Pterodactyl\Contracts\Repository\EggRepositoryInterface;
use Pterodactyl\Contracts\Repository\NodeRepositoryInterface;
use Pterodactyl\Contracts\Repository\ServerRepositoryInterface;
use Pterodactyl\Contracts\Repository\UserRepositoryInterface;
use Pterodactyl\Http\Controllers\Controller; use Pterodactyl\Http\Controllers\Controller;
use Pterodactyl\Contracts\Repository\EggRepositoryInterface;
use Pterodactyl\Traits\Controllers\PlainJavascriptInjection; use Pterodactyl\Traits\Controllers\PlainJavascriptInjection;
use Pterodactyl\Contracts\Repository\NodeRepositoryInterface;
use Pterodactyl\Contracts\Repository\UserRepositoryInterface;
use Pterodactyl\Contracts\Repository\ServerRepositoryInterface;
use Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface;
use Pterodactyl\Contracts\Repository\AllocationRepositoryInterface;
class StatisticsController extends Controller class StatisticsController extends Controller
{ {
@ -29,15 +27,14 @@ class StatisticsController extends Controller
private $userRepository; private $userRepository;
function __construct( public function __construct(
AllocationRepositoryInterface $allocationRepository, AllocationRepositoryInterface $allocationRepository,
DatabaseRepositoryInterface $databaseRepository, DatabaseRepositoryInterface $databaseRepository,
EggRepositoryInterface $eggRepository, EggRepositoryInterface $eggRepository,
NodeRepositoryInterface $nodeRepository, NodeRepositoryInterface $nodeRepository,
ServerRepositoryInterface $serverRepository, ServerRepositoryInterface $serverRepository,
UserRepositoryInterface $userRepository UserRepositoryInterface $userRepository
) ) {
{
$this->allocationRepository = $allocationRepository; $this->allocationRepository = $allocationRepository;
$this->databaseRepository = $databaseRepository; $this->databaseRepository = $databaseRepository;
$this->eggRepository = $eggRepository; $this->eggRepository = $eggRepository;
@ -97,5 +94,4 @@ class StatisticsController extends Controller
'totalAllocations' => $totalAllocations, 'totalAllocations' => $totalAllocations,
]); ]);
} }
} }

View file

@ -3,7 +3,9 @@
namespace Pterodactyl\Http\Controllers\Base; namespace Pterodactyl\Http\Controllers\Base;
use Pterodactyl\Models\User; use Pterodactyl\Models\User;
use Illuminate\Auth\AuthManager;
use Prologue\Alerts\AlertsMessageBag; use Prologue\Alerts\AlertsMessageBag;
use Illuminate\Contracts\Session\Session;
use Pterodactyl\Http\Controllers\Controller; use Pterodactyl\Http\Controllers\Controller;
use Pterodactyl\Services\Users\UserUpdateService; use Pterodactyl\Services\Users\UserUpdateService;
use Pterodactyl\Http\Requests\Base\AccountDataFormRequest; use Pterodactyl\Http\Requests\Base\AccountDataFormRequest;
@ -15,6 +17,11 @@ class AccountController extends Controller
*/ */
protected $alert; protected $alert;
/**
* @var \Illuminate\Auth\SessionGuard
*/
protected $sessionGuard;
/** /**
* @var \Pterodactyl\Services\Users\UserUpdateService * @var \Pterodactyl\Services\Users\UserUpdateService
*/ */
@ -24,12 +31,14 @@ class AccountController extends Controller
* AccountController constructor. * AccountController constructor.
* *
* @param \Prologue\Alerts\AlertsMessageBag $alert * @param \Prologue\Alerts\AlertsMessageBag $alert
* @param \Illuminate\Auth\AuthManager $authManager
* @param \Pterodactyl\Services\Users\UserUpdateService $updateService * @param \Pterodactyl\Services\Users\UserUpdateService $updateService
*/ */
public function __construct(AlertsMessageBag $alert, UserUpdateService $updateService) public function __construct(AlertsMessageBag $alert, AuthManager $authManager, UserUpdateService $updateService)
{ {
$this->alert = $alert; $this->alert = $alert;
$this->updateService = $updateService; $this->updateService = $updateService;
$this->sessionGuard = $authManager->guard();
} }
/** /**
@ -50,21 +59,26 @@ class AccountController extends Controller
* *
* @throws \Pterodactyl\Exceptions\Model\DataValidationException * @throws \Pterodactyl\Exceptions\Model\DataValidationException
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
* @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException
*/ */
public function update(AccountDataFormRequest $request) public function update(AccountDataFormRequest $request)
{ {
$data = []; // Prevent logging this specific session out when the password is changed. This will
// automatically update the user's password anyways, so no need to do anything else here.
if ($request->input('do_action') === 'password') { if ($request->input('do_action') === 'password') {
$data['password'] = $request->input('new_password'); $this->sessionGuard->logoutOtherDevices($request->input('new_password'));
} elseif ($request->input('do_action') === 'email') { } else {
$data['email'] = $request->input('new_email'); if ($request->input('do_action') === 'email') {
} elseif ($request->input('do_action') === 'identity') { $data = ['email' => $request->input('new_email')];
$data = $request->only(['name_first', 'name_last', 'username']); } elseif ($request->input('do_action') === 'identity') {
$data = $request->only(['name_first', 'name_last', 'username']);
} else {
$data = [];
}
$this->updateService->setUserLevel(User::USER_LEVEL_USER);
$this->updateService->handle($request->user(), $data);
} }
$this->updateService->setUserLevel(User::USER_LEVEL_USER);
$this->updateService->handle($request->user(), $data);
$this->alert->success(trans('base.account.details_updated'))->flash(); $this->alert->success(trans('base.account.details_updated'))->flash();
return redirect()->route('account'); return redirect()->route('account');

View file

@ -7,9 +7,26 @@ use Illuminate\Http\Request;
use Pterodactyl\Models\Node; use Pterodactyl\Models\Node;
use Pterodactyl\Models\Server; use Pterodactyl\Models\Server;
use Pterodactyl\Http\Controllers\Controller; use Pterodactyl\Http\Controllers\Controller;
use Pterodactyl\Events\Server\Installed as ServerInstalled;
use Illuminate\Contracts\Events\Dispatcher as EventDispatcher;
class ActionController extends Controller class ActionController extends Controller
{ {
/**
* @var \Illuminate\Contracts\Events\Dispatcher
*/
private $eventDispatcher;
/**
* ActionController constructor.
*
* @param \Illuminate\Contracts\Events\Dispatcher $eventDispatcher
*/
public function __construct(EventDispatcher $eventDispatcher)
{
$this->eventDispatcher = $eventDispatcher;
}
/** /**
* Handles install toggle request from daemon. * Handles install toggle request from daemon.
* *
@ -37,6 +54,11 @@ class ActionController extends Controller
$server->installed = ($status === 'installed') ? 1 : 2; $server->installed = ($status === 'installed') ? 1 : 2;
$server->save(); $server->save();
// Only fire event if server installed successfully.
if ($server->installed === 1) {
$this->eventDispatcher->dispatch(new ServerInstalled($server));
}
return response()->json([]); return response()->json([]);
} }

View file

@ -157,7 +157,6 @@ class SubuserController extends Controller
* @return \Illuminate\Http\RedirectResponse * @return \Illuminate\Http\RedirectResponse
* *
* @throws \Exception * @throws \Exception
* @throws \Illuminate\Auth\Access\AuthorizationException
* @throws \Pterodactyl\Exceptions\Model\DataValidationException * @throws \Pterodactyl\Exceptions\Model\DataValidationException
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
* @throws \Pterodactyl\Exceptions\Service\Subuser\ServerSubuserExistsException * @throws \Pterodactyl\Exceptions\Service\Subuser\ServerSubuserExistsException
@ -171,7 +170,7 @@ class SubuserController extends Controller
$this->alert->success(trans('server.users.user_assigned'))->flash(); $this->alert->success(trans('server.users.user_assigned'))->flash();
return redirect()->route('server.subusers.view', [ return redirect()->route('server.subusers.view', [
'uuid' => $server->uuid, 'uuid' => $server->uuidShort,
'id' => $subuser->hashid, 'id' => $subuser->hashid,
]); ]);
} }

View file

@ -19,6 +19,7 @@ use Pterodactyl\Http\Middleware\Api\AuthenticateKey;
use Illuminate\Routing\Middleware\SubstituteBindings; use Illuminate\Routing\Middleware\SubstituteBindings;
use Pterodactyl\Http\Middleware\AccessingValidServer; use Pterodactyl\Http\Middleware\AccessingValidServer;
use Pterodactyl\Http\Middleware\Api\SetSessionDriver; use Pterodactyl\Http\Middleware\Api\SetSessionDriver;
use Illuminate\Session\Middleware\AuthenticateSession;
use Illuminate\View\Middleware\ShareErrorsFromSession; use Illuminate\View\Middleware\ShareErrorsFromSession;
use Pterodactyl\Http\Middleware\MaintenanceMiddleware; use Pterodactyl\Http\Middleware\MaintenanceMiddleware;
use Pterodactyl\Http\Middleware\RedirectIfAuthenticated; use Pterodactyl\Http\Middleware\RedirectIfAuthenticated;
@ -64,6 +65,7 @@ class Kernel extends HttpKernel
EncryptCookies::class, EncryptCookies::class,
AddQueuedCookiesToResponse::class, AddQueuedCookiesToResponse::class,
StartSession::class, StartSession::class,
AuthenticateSession::class,
ShareErrorsFromSession::class, ShareErrorsFromSession::class,
VerifyCsrfToken::class, VerifyCsrfToken::class,
SubstituteBindings::class, SubstituteBindings::class,

View file

@ -74,7 +74,7 @@ class StoreNodeRequest extends ApplicationApiRequest
$response = parent::validated(); $response = parent::validated();
$response['daemonListen'] = $response['daemon_listen']; $response['daemonListen'] = $response['daemon_listen'];
$response['daemonSFTP'] = $response['daemon_sftp']; $response['daemonSFTP'] = $response['daemon_sftp'];
$response['daemonBase'] = $response['daemon_base']; $response['daemonBase'] = $response['daemon_base'] ?? (new Node)->getAttribute('daemonBase');
unset($response['daemon_base'], $response['daemon_listen'], $response['daemon_sftp']); unset($response['daemon_base'], $response['daemon_listen'], $response['daemon_sftp']);

View file

@ -25,7 +25,7 @@ class SubuserStoreFormRequest extends ServerFormRequest
{ {
return [ return [
'email' => 'required|email', 'email' => 'required|email',
'permissions' => 'present|array', 'permissions' => 'sometimes|array',
]; ];
} }
} }

View file

View file

@ -23,7 +23,7 @@ class AccountCreated extends Notification implements ShouldQueue
/** /**
* The user model for the created user. * The user model for the created user.
* *
* @var object * @var \Pterodactyl\Models\User
*/ */
public $user; public $user;
@ -65,7 +65,7 @@ class AccountCreated extends Notification implements ShouldQueue
->line('Email: ' . $this->user->email); ->line('Email: ' . $this->user->email);
if (! is_null($this->token)) { if (! is_null($this->token)) {
return $message->action('Setup Your Account', url('/auth/password/reset/' . $this->token . '?email=' . $this->user->email)); return $message->action('Setup Your Account', url('/auth/password/reset/' . $this->token . '?email=' . urlencode($this->user->email)));
} }
return $message; return $message;

View file

@ -0,0 +1,69 @@
<?php
namespace Pterodactyl\Notifications;
use Illuminate\Bus\Queueable;
use Pterodactyl\Events\Event;
use Illuminate\Container\Container;
use Illuminate\Notifications\Notification;
use Illuminate\Contracts\Queue\ShouldQueue;
use Pterodactyl\Contracts\Core\ReceivesEvents;
use Illuminate\Contracts\Notifications\Dispatcher;
use Illuminate\Notifications\Messages\MailMessage;
class ServerInstalled extends Notification implements ShouldQueue, ReceivesEvents
{
use Queueable;
/**
* @var \Pterodactyl\Models\Server
*/
public $server;
/**
* @var \Pterodactyl\Models\User
*/
public $user;
/**
* Handle a direct call to this notification from the server installed event. This is configured
* in the event service provider.
*
* @param \Pterodactyl\Events\Event|\Pterodactyl\Events\Server\Installed $event
*/
public function handle(Event $event): void
{
$event->server->loadMissing('user');
$this->server = $event->server;
$this->user = $event->server->user;
// Since we are calling this notification directly from an event listener we need to fire off the dispatcher
// to send the email now. Don't use send() or you'll end up firing off two different events.
Container::getInstance()->make(Dispatcher::class)->sendNow($this->user, $this);
}
/**
* Get the notification's delivery channels.
*
* @return array
*/
public function via()
{
return ['mail'];
}
/**
* Get the mail representation of the notification.
*
* @return \Illuminate\Notifications\Messages\MailMessage
*/
public function toMail()
{
return (new MailMessage)
->greeting('Hello ' . $this->user->username . '.')
->line('Your server has finished installing and is now ready for you to use.')
->line('Server Name: ' . $this->server->name)
->action('Login and Begin Using', route('index'));
}
}

View file

@ -2,6 +2,8 @@
namespace Pterodactyl\Providers; namespace Pterodactyl\Providers;
use Pterodactyl\Events\Server\Installed as ServerInstalledEvent;
use Pterodactyl\Notifications\ServerInstalled as ServerInstalledNotification;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider; use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
class EventServiceProvider extends ServiceProvider class EventServiceProvider extends ServiceProvider
@ -11,5 +13,9 @@ class EventServiceProvider extends ServiceProvider
* *
* @var array * @var array
*/ */
protected $listen = []; protected $listen = [
ServerInstalledEvent::class => [
ServerInstalledNotification::class,
],
];
} }

View file

@ -144,6 +144,7 @@ abstract class BaseRepository implements BaseRepositoryInterface
$headers['X-Access-Token'] = $this->getToken() ?? $this->getNode()->daemonSecret; $headers['X-Access-Token'] = $this->getToken() ?? $this->getNode()->daemonSecret;
return new Client([ return new Client([
'verify' => config('app.env') === 'production',
'base_uri' => sprintf('%s://%s:%s/v1/', $this->getNode()->scheme, $this->getNode()->fqdn, $this->getNode()->daemonListen), 'base_uri' => sprintf('%s://%s:%s/v1/', $this->getNode()->scheme, $this->getNode()->fqdn, $this->getNode()->daemonListen),
'timeout' => config('pterodactyl.guzzle.timeout'), 'timeout' => config('pterodactyl.guzzle.timeout'),
'connect_timeout' => config('pterodactyl.guzzle.connect_timeout'), 'connect_timeout' => config('pterodactyl.guzzle.connect_timeout'),

View file

@ -298,7 +298,7 @@ abstract class EloquentRepository extends Repository implements RepositoryInterf
} }
/** /**
* Get the amount of entries in the database * Get the amount of entries in the database.
* *
* @return int * @return int
*/ */

View file

@ -330,7 +330,7 @@ class ServerRepository extends EloquentRepository implements ServerRepositoryInt
} }
/** /**
* Get the amount of servers that are suspended * Get the amount of servers that are suspended.
* *
* @return int * @return int
*/ */

View file

@ -1,59 +0,0 @@
<?php
/**
* Pterodactyl - Panel
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>.
*
* This software is licensed under the terms of the MIT license.
* https://opensource.org/licenses/MIT
*/
namespace Pterodactyl\Services\Helpers;
use Ramsey\Uuid\Uuid;
use Illuminate\Contracts\Hashing\Hasher;
use Illuminate\Database\ConnectionInterface;
class TemporaryPasswordService
{
const HMAC_ALGO = 'sha256';
/**
* @var \Illuminate\Database\ConnectionInterface
*/
protected $connection;
/**
* @var \Illuminate\Contracts\Hashing\Hasher
*/
protected $hasher;
/**
* TemporaryPasswordService constructor.
*
* @param \Illuminate\Database\ConnectionInterface $connection
* @param \Illuminate\Contracts\Hashing\Hasher $hasher
*/
public function __construct(ConnectionInterface $connection, Hasher $hasher)
{
$this->connection = $connection;
$this->hasher = $hasher;
}
/**
* Store a password reset token for a specific email address.
*
* @param string $email
* @return string
*/
public function handle($email)
{
$token = hash_hmac(self::HMAC_ALGO, Uuid::uuid4()->toString(), config('app.key'));
$this->connection->table('password_resets')->insert([
'email' => $email,
'token' => $this->hasher->make($token),
]);
return $token;
}
}

View file

@ -78,7 +78,7 @@ class EnvironmentService
} }
// Process variables set in the configuration file. // Process variables set in the configuration file.
foreach ($this->config->get('pterodactyl.environment_mappings', []) as $key => $object) { foreach ($this->config->get('pterodactyl.environment_variables', []) as $key => $object) {
if (is_callable($object)) { if (is_callable($object)) {
$variables[$key] = call_user_func($object, $server); $variables[$key] = call_user_func($object, $server);
} else { } else {

View file

@ -56,6 +56,8 @@ class PermissionCreationService
} }
} }
$this->repository->withoutFreshModel()->insert($insertPermissions); if (! empty($insertPermissions)) {
$this->repository->withoutFreshModel()->insert($insertPermissions);
}
} }
} }

View file

@ -5,8 +5,8 @@ namespace Pterodactyl\Services\Users;
use Ramsey\Uuid\Uuid; use Ramsey\Uuid\Uuid;
use Illuminate\Contracts\Hashing\Hasher; use Illuminate\Contracts\Hashing\Hasher;
use Illuminate\Database\ConnectionInterface; use Illuminate\Database\ConnectionInterface;
use Illuminate\Contracts\Auth\PasswordBroker;
use Pterodactyl\Notifications\AccountCreated; use Pterodactyl\Notifications\AccountCreated;
use Pterodactyl\Services\Helpers\TemporaryPasswordService;
use Pterodactyl\Contracts\Repository\UserRepositoryInterface; use Pterodactyl\Contracts\Repository\UserRepositoryInterface;
class UserCreationService class UserCreationService
@ -22,9 +22,9 @@ class UserCreationService
private $hasher; private $hasher;
/** /**
* @var \Pterodactyl\Services\Helpers\TemporaryPasswordService * @var \Illuminate\Contracts\Auth\PasswordBroker
*/ */
private $passwordService; private $passwordBroker;
/** /**
* @var \Pterodactyl\Contracts\Repository\UserRepositoryInterface * @var \Pterodactyl\Contracts\Repository\UserRepositoryInterface
@ -36,18 +36,18 @@ class UserCreationService
* *
* @param \Illuminate\Database\ConnectionInterface $connection * @param \Illuminate\Database\ConnectionInterface $connection
* @param \Illuminate\Contracts\Hashing\Hasher $hasher * @param \Illuminate\Contracts\Hashing\Hasher $hasher
* @param \Pterodactyl\Services\Helpers\TemporaryPasswordService $passwordService * @param \Illuminate\Contracts\Auth\PasswordBroker $passwordBroker
* @param \Pterodactyl\Contracts\Repository\UserRepositoryInterface $repository * @param \Pterodactyl\Contracts\Repository\UserRepositoryInterface $repository
*/ */
public function __construct( public function __construct(
ConnectionInterface $connection, ConnectionInterface $connection,
Hasher $hasher, Hasher $hasher,
TemporaryPasswordService $passwordService, PasswordBroker $passwordBroker,
UserRepositoryInterface $repository UserRepositoryInterface $repository
) { ) {
$this->connection = $connection; $this->connection = $connection;
$this->hasher = $hasher; $this->hasher = $hasher;
$this->passwordService = $passwordService; $this->passwordBroker = $passwordBroker;
$this->repository = $repository; $this->repository = $repository;
} }
@ -68,8 +68,8 @@ class UserCreationService
$this->connection->beginTransaction(); $this->connection->beginTransaction();
if (! isset($data['password']) || empty($data['password'])) { if (! isset($data['password']) || empty($data['password'])) {
$generateResetToken = true;
$data['password'] = $this->hasher->make(str_random(30)); $data['password'] = $this->hasher->make(str_random(30));
$token = $this->passwordService->handle($data['email']);
} }
/** @var \Pterodactyl\Models\User $user */ /** @var \Pterodactyl\Models\User $user */
@ -77,6 +77,10 @@ class UserCreationService
'uuid' => Uuid::uuid4()->toString(), 'uuid' => Uuid::uuid4()->toString(),
]), true, true); ]), true, true);
if (isset($generateResetToken)) {
$token = $this->passwordBroker->createToken($user);
}
$this->connection->commit(); $this->connection->commit();
$user->notify(new AccountCreated($user, $token ?? null)); $user->notify(new AccountCreated($user, $token ?? null));

View file

@ -3,7 +3,7 @@
* Created by PhpStorm. * Created by PhpStorm.
* User: Stan * User: Stan
* Date: 26-5-2018 * Date: 26-5-2018
* Time: 20:56 * Time: 20:56.
*/ */
namespace Pterodactyl\Traits\Controllers; namespace Pterodactyl\Traits\Controllers;
@ -12,13 +12,11 @@ use JavaScript;
trait PlainJavascriptInjection trait PlainJavascriptInjection
{ {
/** /**
* Injects statistics into javascript * Injects statistics into javascript.
*/ */
public function injectJavascript($data) public function injectJavascript($data)
{ {
Javascript::put($data); Javascript::put($data);
} }
} }

View file

@ -1,8 +1,6 @@
coverage: coverage:
status: status:
project: project: off
default: patch: off
target: 35
threshold: 1
comment: comment:
layout: "diff" layout: "diff"

561
composer.lock generated

File diff suppressed because it is too large Load diff

View file

@ -34,6 +34,7 @@ return [
'mysql' => [ 'mysql' => [
'driver' => 'mysql', 'driver' => 'mysql',
'host' => env('DB_HOST', '127.0.0.1'), 'host' => env('DB_HOST', '127.0.0.1'),
'unix_socket' => env('DB_SOCKET'),
'port' => env('DB_PORT', '3306'), 'port' => env('DB_PORT', '3306'),
'database' => env('DB_DATABASE', 'panel'), 'database' => env('DB_DATABASE', 'panel'),
'username' => env('DB_USERNAME', 'pterodactyl'), 'username' => env('DB_USERNAME', 'pterodactyl'),

View file

@ -14,7 +14,7 @@ class AddMaintenanceToNodes extends Migration
public function up() public function up()
{ {
Schema::table('nodes', function (Blueprint $table) { Schema::table('nodes', function (Blueprint $table) {
$table->boolean('maintenance_mode')->after('behind_proxy')->default(false); $table->boolean('maintenance_mode')->after('behind_proxy')->default(false);
}); });
} }
@ -26,7 +26,7 @@ class AddMaintenanceToNodes extends Migration
public function down() public function down()
{ {
Schema::table('nodes', function (Blueprint $table) { Schema::table('nodes', function (Blueprint $table) {
$table->dropColumn('maintenance_mode'); $table->dropColumn('maintenance_mode');
}); });
} }
} }

View file

@ -3,7 +3,7 @@
"meta": { "meta": {
"version": "PTDL_v1" "version": "PTDL_v1"
}, },
"exported_at": "2018-02-27T00:57:04-06:00", "exported_at": "2018-06-25T15:47:07-04:00",
"name": "Forge Minecraft", "name": "Forge Minecraft",
"author": "support@pterodactyl.io", "author": "support@pterodactyl.io",
"description": "Minecraft Forge Server. Minecraft Forge is a modding API (Application Programming Interface), which makes it easier to create mods, and also make sure mods are compatible with each other.", "description": "Minecraft Forge Server. Minecraft Forge is a modding API (Application Programming Interface), which makes it easier to create mods, and also make sure mods are compatible with each other.",
@ -17,7 +17,7 @@
}, },
"scripts": { "scripts": {
"installation": { "installation": {
"script": "#!\/bin\/ash\r\n# Forge Installation Script\r\n#\r\n# Server Files: \/mnt\/server\r\napk update\r\napk add curl\r\n\r\nGET_VERSIONS=$(curl -sl http:\/\/files.minecraftforge.net\/maven\/net\/minecraftforge\/forge\/ | grep -A1 Latest | grep -o -e '[1]\\.[0-9][0-9]]\\?\\.\\?[0-9]\\?[0-9] - [0-9][0-9]\\.[0-9][0-9]\\.[0-9]\\?[0-9]\\.[0-9][0-9][0-9][0-9]')\r\nLATEST_VERSION=$(echo $GET_VERSIONS | sed 's\/ \/\/g')\r\n\r\ncd \/mnt\/server\r\n\r\ncurl -sS http:\/\/files.minecraftforge.net\/maven\/net\/minecraftforge\/forge\/$LATEST_VERSION\/forge-$LATEST_VERSION-installer.jar -o installer.jar\r\ncurl -sS http:\/\/files.minecraftforge.net\/maven\/net\/minecraftforge\/forge\/$LATEST_VERSION\/forge-$LATEST_VERSION-universal.jar -o server.jar\r\n\r\njava -jar installer.jar --installServer\r\nrm -rf installer.jar", "script": "#!\/bin\/ash\r\n# Forge Installation Script\r\n#\r\n# Server Files: \/mnt\/server\r\napk update\r\napk add curl\r\n\r\nif [ -z \"$MC_VERSION\" ] || [ \"$MC_VERSION\" == \"latest\" ]; then\r\n FORGE_VERSION=$(curl -sl http:\/\/files.minecraftforge.net\/maven\/net\/minecraftforge\/forge\/ | grep -A1 Latest | grep -o -e '[1]\\.[0-9][0-9]]\\?\\.\\?[0-9]\\?[0-9] - [0-9][0-9]\\.[0-9][0-9]\\.[0-9]\\?[0-9]\\.[0-9][0-9][0-9][0-9]' | sed 's\/ \/\/g')\r\nelse\r\n FORGE_VERSION=$(curl -sl http:\/\/files.minecraftforge.net\/maven\/net\/minecraftforge\/forge\/index_$MC_VERSION.html | grep -A1 Latest | grep -o -e '[1]\\.[0-9][0-9]]\\?\\.\\?[0-9]\\?[0-9] - [0-9][0-9]\\.[0-9][0-9]\\.[0-9]\\?[0-9]\\.[0-9][0-9][0-9][0-9]' | sed 's\/ \/\/g')\r\nfi\r\n\r\ncd \/mnt\/server\r\n\r\necho -e \"\\nDownloading Forge Version $FORGE_VERSION\\n\"\r\ncurl -sS http:\/\/files.minecraftforge.net\/maven\/net\/minecraftforge\/forge\/$FORGE_VERSION\/forge-$FORGE_VERSION-installer.jar -o installer.jar\r\ncurl -sS http:\/\/files.minecraftforge.net\/maven\/net\/minecraftforge\/forge\/$FORGE_VERSION\/forge-$FORGE_VERSION-universal.jar -o $SERVER_JARFILE\r\n\r\necho -e \"\\nInstalling forge server usint the installer jar file.\\n\"\r\njava -jar installer.jar --installServer\r\n\r\necho -e \"\\nDeleting installer jar file and cleaning up.\\n\"\r\nrm -rf installer.jar",
"container": "frolvlad\/alpine-oraclejdk8:cleaned", "container": "frolvlad\/alpine-oraclejdk8:cleaned",
"entrypoint": "ash" "entrypoint": "ash"
} }
@ -31,6 +31,15 @@
"user_viewable": 1, "user_viewable": 1,
"user_editable": 1, "user_editable": 1,
"rules": "required|regex:\/^([\\w\\d._-]+)(\\.jar)$\/" "rules": "required|regex:\/^([\\w\\d._-]+)(\\.jar)$\/"
},
{
"name": "Minecraft version",
"description": "The version of minecraft that you want to run. Example (1.10.2).",
"env_variable": "MC_VERSION",
"default_value": "latest",
"user_viewable": 1,
"user_editable": 1,
"rules": "required|string|max:20"
} }
] ]
} }

View file

@ -3,7 +3,7 @@
"meta": { "meta": {
"version": "PTDL_v1" "version": "PTDL_v1"
}, },
"exported_at": "2017-11-03T22:15:07-05:00", "exported_at": "2018-06-19T17:09:18-04:00",
"name": "Vanilla Minecraft", "name": "Vanilla Minecraft",
"author": "support@pterodactyl.io", "author": "support@pterodactyl.io",
"description": "Minecraft is a game about placing blocks and going on adventures. Explore randomly generated worlds and build amazing things from the simplest of homes to the grandest of castles. Play in Creative Mode with unlimited resources or mine deep in Survival Mode, crafting weapons and armor to fend off dangerous mobs. Do all this alone or with friends.", "description": "Minecraft is a game about placing blocks and going on adventures. Explore randomly generated worlds and build amazing things from the simplest of homes to the grandest of castles. Play in Creative Mode with unlimited resources or mine deep in Survival Mode, crafting weapons and armor to fend off dangerous mobs. Do all this alone or with friends.",
@ -17,8 +17,8 @@
}, },
"scripts": { "scripts": {
"installation": { "installation": {
"script": "#!\/bin\/ash\r\n# Vanilla MC Installation Script\r\n#\r\n# Server Files: \/mnt\/server\r\napk update\r\napk add curl\r\n\r\ncd \/mnt\/server\r\n\r\nLATEST_VERSION=`curl -s https:\/\/s3.amazonaws.com\/Minecraft.Download\/versions\/versions.json | grep -o \"[[:digit:]]\\.[0-9]*\\.[0-9]\" | head -n 1`\r\n\r\nif [ -z \"$VANILLA_VERSION\" ] || [ \"$VANILLA_VERSION\" == \"latest\" ]; then\r\n DL_VERSION=$LATEST_VERSION\r\nelse\r\n DL_VERSION=$VANILLA_VERSION\r\nfi\r\n\r\ncurl -o ${SERVER_JARFILE} https:\/\/s3.amazonaws.com\/Minecraft.Download\/versions\/${DL_VERSION}\/minecraft_server.${DL_VERSION}.jar", "script": "#!\/bin\/ash\r\n# Vanilla MC Installation Script\r\n#\r\n# Server Files: \/mnt\/server\r\napk update\r\napk add curl jq\r\n\r\ncd \/mnt\/server\r\n\r\nLATEST_VERSION=`curl https:\/\/launchermeta.mojang.com\/mc\/game\/version_manifest.json | jq -r '.latest.release'`\r\n\r\nif [ -z \"$VANILLA_VERSION\" ] || [ \"$VANILLA_VERSION\" == \"latest\" ]; then\r\n MANIFEST_URL=$(curl https:\/\/launchermeta.mojang.com\/mc\/game\/version_manifest.json | jq .versions | jq -r '.[] | select(.id == \"'$LATEST_VERSION'\") | .url')\r\nelse\r\n MANIFEST_URL=$(curl https:\/\/launchermeta.mojang.com\/mc\/game\/version_manifest.json | jq .versions | jq -r '.[] | select(.id == \"'$VANILLA_VERSION'\") | .url')\r\nfi\r\n\r\nDOWNLOAD_URL=`curl $MANIFEST_URL | jq .downloads.server | jq -r '. | .url'`\r\n\r\ncurl -o ${SERVER_JARFILE} $DOWNLOAD_URL",
"container": "alpine:3.4", "container": "alpine:3.7",
"entrypoint": "ash" "entrypoint": "ash"
} }
}, },
@ -39,7 +39,7 @@
"default_value": "latest", "default_value": "latest",
"user_viewable": 1, "user_viewable": 1,
"user_editable": 1, "user_editable": 1,
"rules": "required|string|between:3,7" "rules": "required|string|between:3,15"
} }
] ]
} }

View file

@ -3,7 +3,7 @@
"meta": { "meta": {
"version": "PTDL_v1" "version": "PTDL_v1"
}, },
"exported_at": "2018-01-21T16:59:47-06:00", "exported_at": "2018-06-19T07:46:06-04:00",
"name": "Counter-Strike: Global Offensive", "name": "Counter-Strike: Global Offensive",
"author": "support@pterodactyl.io", "author": "support@pterodactyl.io",
"description": "Counter-Strike: Global Offensive is a multiplayer first-person shooter video game developed by Hidden Path Entertainment and Valve Corporation.", "description": "Counter-Strike: Global Offensive is a multiplayer first-person shooter video game developed by Hidden Path Entertainment and Valve Corporation.",
@ -11,7 +11,7 @@
"startup": ".\/srcds_run -game csgo -console -port {{SERVER_PORT}} +ip 0.0.0.0 +map {{SRCDS_MAP}} -strictportbind -norestart +sv_setsteamaccount {{STEAM_ACC}}", "startup": ".\/srcds_run -game csgo -console -port {{SERVER_PORT}} +ip 0.0.0.0 +map {{SRCDS_MAP}} -strictportbind -norestart +sv_setsteamaccount {{STEAM_ACC}}",
"config": { "config": {
"files": "{}", "files": "{}",
"startup": "{\r\n \"done\": \"gameserver Steam ID\",\r\n \"userInteraction\": []\r\n}", "startup": "{\r\n \"done\": \"Connection to Steam servers successful\",\r\n \"userInteraction\": []\r\n}",
"logs": "{\r\n \"custom\": true,\r\n \"location\": \"logs\/latest.log\"\r\n}", "logs": "{\r\n \"custom\": true,\r\n \"location\": \"logs\/latest.log\"\r\n}",
"stop": "quit" "stop": "quit"
}, },
@ -40,6 +40,15 @@
"user_viewable": 1, "user_viewable": 1,
"user_editable": 1, "user_editable": 1,
"rules": "required|string|alpha_num|size:32" "rules": "required|string|alpha_num|size:32"
},
{
"name": "Source AppID",
"description": "Required for game to update on server restart. Do not modify this.",
"env_variable": "SRCDS_APPID",
"default_value": "740",
"user_viewable": 0,
"user_editable": 0,
"rules": "required|string|max:20"
} }
] ]
} }

View file

@ -3,7 +3,7 @@
"meta": { "meta": {
"version": "PTDL_v1" "version": "PTDL_v1"
}, },
"exported_at": "2018-01-21T16:59:47-06:00", "exported_at": "2018-06-19T07:46:27-04:00",
"name": "Garrys Mod", "name": "Garrys Mod",
"author": "support@pterodactyl.io", "author": "support@pterodactyl.io",
"description": "Garrys Mod, is a sandbox physics game created by Garry Newman, and developed by his company, Facepunch Studios.", "description": "Garrys Mod, is a sandbox physics game created by Garry Newman, and developed by his company, Facepunch Studios.",
@ -40,6 +40,15 @@
"user_viewable": 1, "user_viewable": 1,
"user_editable": 1, "user_editable": 1,
"rules": "required|string|alpha_num|size:32" "rules": "required|string|alpha_num|size:32"
},
{
"name": "Source AppID",
"description": "Required for game to update on server restart. Do not modify this.",
"env_variable": "SRCDS_APPID",
"default_value": "4020",
"user_viewable": 0,
"user_editable": 0,
"rules": "required|string|max:20"
} }
] ]
} }

View file

@ -474,6 +474,11 @@ label.control-label > span.field-optional:before {
width: auto; width: auto;
} }
.search01 {
width: 30%;
}
.number-info-box-content { .number-info-box-content {
padding: 15px 10px 0; padding: 15px 10px 0;
} }

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -55,6 +55,10 @@ class ActionsClass {
showLoaderOnConfirm: true, showLoaderOnConfirm: true,
inputValue: inputValue inputValue: inputValue
}, (val) => { }, (val) => {
if (val === false) {
return false;
}
$.ajax({ $.ajax({
type: 'POST', type: 'POST',
headers: { headers: {
@ -100,6 +104,10 @@ class ActionsClass {
showLoaderOnConfirm: true, showLoaderOnConfirm: true,
inputValue: `${currentPath}${currentName}`, inputValue: `${currentPath}${currentName}`,
}, (val) => { }, (val) => {
if (val === false) {
return false;
}
$.ajax({ $.ajax({
type: 'POST', type: 'POST',
headers: { headers: {
@ -233,6 +241,10 @@ class ActionsClass {
showLoaderOnConfirm: true, showLoaderOnConfirm: true,
inputValue: `${currentPath}${currentName}`, inputValue: `${currentPath}${currentName}`,
}, (val) => { }, (val) => {
if (val === false) {
return false;
}
$.ajax({ $.ajax({
type: 'POST', type: 'POST',
headers: { headers: {

View file

@ -48,7 +48,7 @@ return [
'select_none' => 'Alles abwählen', 'select_none' => 'Alles abwählen',
'alias' => 'Alias', 'alias' => 'Alias',
'primary' => 'Primär', 'primary' => 'Primär',
'make_primary' => 'Primät machen', 'make_primary' => 'Primär machen',
'none' => 'Nichts', 'none' => 'Nichts',
'cancel' => 'Abbrechen', 'cancel' => 'Abbrechen',
'created_at' => 'Erstellt am', 'created_at' => 'Erstellt am',

View file

@ -62,7 +62,7 @@
<h3 class="box-title">Force Delete Server</h3> <h3 class="box-title">Force Delete Server</h3>
</div> </div>
<div class="box-body"> <div class="box-body">
<p>This action will attempt to delete the server from both the panel and daemon. The the daemon does not respond, or reports an error the deletion will continue.</p> <p>This action will attempt to delete the server from both the panel and daemon. If the daemon does not respond, or reports an error the deletion will continue.</p>
<p class="text-danger small">Deleting a server is an irreversible action. <strong>All server data</strong> (including files and users) will be removed from the system. This method may leave dangling files on your daemon if it reports an error.</p> <p class="text-danger small">Deleting a server is an irreversible action. <strong>All server data</strong> (including files and users) will be removed from the system. This method may leave dangling files on your daemon if it reports an error.</p>
</div> </div>
<div class="box-footer"> <div class="box-footer">

View file

@ -23,10 +23,10 @@
<div class="box"> <div class="box">
<div class="box-header"> <div class="box-header">
<h3 class="box-title">@lang('base.index.list')</h3> <h3 class="box-title">@lang('base.index.list')</h3>
<div class="box-tools"> <div class="box-tools search01">
<form action="{{ route('index') }}" method="GET"> <form action="{{ route('index') }}" method="GET">
<div class="input-group input-group-sm"> <div class="input-group input-group-sm">
<input type="text" name="query" class="form-control pull-right" style="width:30%;" value="{{ request()->input('query') }}" placeholder="@lang('strings.search')"> <input type="text" name="query" class="form-control pull-right" value="{{ request()->input('query') }}" placeholder="@lang('strings.search')">
<div class="input-group-btn"> <div class="input-group-btn">
<button type="submit" class="btn btn-default"><i class="fa fa-search"></i></button> <button type="submit" class="btn btn-default"><i class="fa fa-search"></i></button>
</div> </div>

View file

@ -146,7 +146,7 @@
</a> </a>
</li> </li>
@endcan @endcan
@can('list-tasks', $server) @can('list-schedules', $server)
<li <li
@if(starts_with(Route::currentRouteName(), 'server.schedules')) @if(starts_with(Route::currentRouteName(), 'server.schedules'))
class="active" class="active"

View file

@ -44,7 +44,7 @@
{!! Theme::js('vendor/lodash/lodash.js') !!} {!! Theme::js('vendor/lodash/lodash.js') !!}
{!! Theme::js('vendor/siofu/client.min.js') !!} {!! Theme::js('vendor/siofu/client.min.js') !!}
@if(App::environment('production')) @if(App::environment('production'))
{!! Theme::js('js/frontend/files/filemanager.min.js') !!} {!! Theme::js('js/frontend/files/filemanager.min.js?updated-cancel-buttons') !!}
@else @else
{!! Theme::js('js/frontend/files/src/index.js') !!} {!! Theme::js('js/frontend/files/src/index.js') !!}
{!! Theme::js('js/frontend/files/src/contextmenu.js') !!} {!! Theme::js('js/frontend/files/src/contextmenu.js') !!}

View file

@ -3,24 +3,22 @@
* Created by PhpStorm. * Created by PhpStorm.
* User: Stan * User: Stan
* Date: 26-5-2018 * Date: 26-5-2018
* Time: 21:06 * Time: 21:06.
*/ */
namespace Tests\Unit\Http\Controllers\Admin; namespace Tests\Unit\Http\Controllers\Admin;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Routing\Controller;
use Mockery as m; use Mockery as m;
use Pterodactyl\Contracts\Repository\AllocationRepositoryInterface;
use Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface;
use Pterodactyl\Contracts\Repository\EggRepositoryInterface;
use Pterodactyl\Contracts\Repository\NodeRepositoryInterface;
use Pterodactyl\Contracts\Repository\ServerRepositoryInterface;
use Pterodactyl\Contracts\Repository\UserRepositoryInterface;
use Pterodactyl\Http\Controllers\Admin\StatisticsController;
use Pterodactyl\Models\Node; use Pterodactyl\Models\Node;
use Tests\Assertions\ControllerAssertionsTrait; use Tests\Assertions\ControllerAssertionsTrait;
use Tests\Unit\Http\Controllers\ControllerTestCase; use Tests\Unit\Http\Controllers\ControllerTestCase;
use Pterodactyl\Contracts\Repository\EggRepositoryInterface;
use Pterodactyl\Http\Controllers\Admin\StatisticsController;
use Pterodactyl\Contracts\Repository\NodeRepositoryInterface;
use Pterodactyl\Contracts\Repository\UserRepositoryInterface;
use Pterodactyl\Contracts\Repository\ServerRepositoryInterface;
use Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface;
use Pterodactyl\Contracts\Repository\AllocationRepositoryInterface;
class StatisticsControllerTest extends ControllerTestCase class StatisticsControllerTest extends ControllerTestCase
{ {
@ -88,7 +86,7 @@ class StatisticsControllerTest extends ControllerTestCase
'disk' => [ 'disk' => [
'value' => 1024, 'value' => 1024,
'max' => 512, 'max' => 512,
] ],
]); ]);
$controller->shouldReceive('injectJavascript')->once(); $controller->shouldReceive('injectJavascript')->once();
@ -106,8 +104,7 @@ class StatisticsControllerTest extends ControllerTestCase
$this->eggRepository, $this->eggRepository,
$this->nodeRepository, $this->nodeRepository,
$this->serverRepository, $this->serverRepository,
$this->userRepository] $this->userRepository, ]
); );
} }
} }

View file

@ -4,6 +4,8 @@ namespace Tests\Unit\Http\Controllers\Base;
use Mockery as m; use Mockery as m;
use Pterodactyl\Models\User; use Pterodactyl\Models\User;
use Illuminate\Auth\AuthManager;
use Illuminate\Auth\SessionGuard;
use Prologue\Alerts\AlertsMessageBag; use Prologue\Alerts\AlertsMessageBag;
use Pterodactyl\Services\Users\UserUpdateService; use Pterodactyl\Services\Users\UserUpdateService;
use Tests\Unit\Http\Controllers\ControllerTestCase; use Tests\Unit\Http\Controllers\ControllerTestCase;
@ -17,6 +19,16 @@ class AccountControllerTest extends ControllerTestCase
*/ */
protected $alert; protected $alert;
/**
* @var \Illuminate\Auth\AuthManager|\Mockery\Mock
*/
protected $authManager;
/**
* @var \Illuminate\Auth\SessionGuard|\Mockery\Mock
*/
protected $sessionGuard;
/** /**
* @var \Pterodactyl\Services\Users\UserUpdateService|\Mockery\Mock * @var \Pterodactyl\Services\Users\UserUpdateService|\Mockery\Mock
*/ */
@ -31,6 +43,10 @@ class AccountControllerTest extends ControllerTestCase
$this->alert = m::mock(AlertsMessageBag::class); $this->alert = m::mock(AlertsMessageBag::class);
$this->updateService = m::mock(UserUpdateService::class); $this->updateService = m::mock(UserUpdateService::class);
$this->authManager = m::mock(AuthManager::class);
$this->sessionGuard = m::mock(SessionGuard::class);
$this->authManager->shouldReceive('guard')->once()->andReturn($this->sessionGuard);
} }
/** /**
@ -50,13 +66,11 @@ class AccountControllerTest extends ControllerTestCase
public function testUpdateControllerForPassword() public function testUpdateControllerForPassword()
{ {
$this->setRequestMockClass(AccountDataFormRequest::class); $this->setRequestMockClass(AccountDataFormRequest::class);
$user = $this->generateRequestUserModel();
$this->request->shouldReceive('input')->with('do_action')->andReturn('password'); $this->request->shouldReceive('input')->with('do_action')->andReturn('password');
$this->request->shouldReceive('input')->with('new_password')->once()->andReturn('test-password'); $this->request->shouldReceive('input')->with('new_password')->once()->andReturn('test-password');
$this->sessionGuard->shouldReceive('logoutOtherDevices')->once()->with('test-password')->andReturnSelf();
$this->updateService->shouldReceive('setUserLevel')->with(User::USER_LEVEL_USER)->once()->andReturnNull();
$this->updateService->shouldReceive('handle')->with($user, ['password' => 'test-password'])->once()->andReturn(collect());
$this->alert->shouldReceive('success->flash')->once()->andReturnNull(); $this->alert->shouldReceive('success->flash')->once()->andReturnNull();
$response = $this->getController()->update($this->request); $response = $this->getController()->update($this->request);
@ -113,6 +127,6 @@ class AccountControllerTest extends ControllerTestCase
*/ */
private function getController(): AccountController private function getController(): AccountController
{ {
return new AccountController($this->alert, $this->updateService); return new AccountController($this->alert, $this->authManager, $this->updateService);
} }
} }

View file

@ -182,7 +182,7 @@ class SubuserControllerTest extends ControllerTestCase
$response = $controller->store($this->request); $response = $controller->store($this->request);
$this->assertIsRedirectResponse($response); $this->assertIsRedirectResponse($response);
$this->assertRedirectRouteEquals('server.subusers.view', $response, [ $this->assertRedirectRouteEquals('server.subusers.view', $response, [
'uuid' => $server->uuid, 'uuid' => $server->uuidShort,
'id' => $subuser->hashid, 'id' => $subuser->hashid,
]); ]);
} }

View file

@ -1,62 +0,0 @@
<?php
namespace Tests\Unit\Services\Helpers;
use Mockery as m;
use Tests\TestCase;
use Tests\Traits\MocksUuids;
use Illuminate\Contracts\Hashing\Hasher;
use Illuminate\Database\ConnectionInterface;
use Pterodactyl\Services\Helpers\TemporaryPasswordService;
class TemporaryPasswordServiceTest extends TestCase
{
use MocksUuids;
/**
* @var \Illuminate\Database\ConnectionInterface|\Mockery\Mock
*/
protected $connection;
/**
* @var \Illuminate\Contracts\Hashing\Hasher|\Mockery\Mock
*/
protected $hasher;
/**
* @var \Pterodactyl\Services\Helpers\TemporaryPasswordService
*/
protected $service;
/**
* Setup tests.
*/
public function setUp()
{
parent::setUp();
$this->connection = m::mock(ConnectionInterface::class);
$this->hasher = m::mock(Hasher::class);
$this->service = new TemporaryPasswordService($this->connection, $this->hasher);
}
/**
* Test that a temporary password is stored and the token is returned.
*/
public function testTemporaryPasswordIsStored()
{
$token = hash_hmac(TemporaryPasswordService::HMAC_ALGO, $this->getKnownUuid(), config('app.key'));
$this->hasher->shouldReceive('make')->with($token)->once()->andReturn('hashed_token');
$this->connection->shouldReceive('table')->with('password_resets')->once()->andReturnSelf();
$this->connection->shouldReceive('insert')->with([
'email' => 'test@example.com',
'token' => 'hashed_token',
])->once()->andReturnNull();
$response = $this->service->handle('test@example.com');
$this->assertNotEmpty($response);
$this->assertEquals($token, $response);
}
}

View file

@ -12,7 +12,7 @@ use Pterodactyl\Contracts\Repository\ServerRepositoryInterface;
class EnvironmentServiceTest extends TestCase class EnvironmentServiceTest extends TestCase
{ {
const CONFIG_MAPPING = 'pterodactyl.environment_mappings'; const CONFIG_MAPPING = 'pterodactyl.environment_variables';
/** /**
* @var \Illuminate\Contracts\Config\Repository|\Mockery\Mock * @var \Illuminate\Contracts\Config\Repository|\Mockery\Mock

View file

@ -9,9 +9,9 @@ use Tests\Traits\MocksUuids;
use Illuminate\Contracts\Hashing\Hasher; use Illuminate\Contracts\Hashing\Hasher;
use Illuminate\Database\ConnectionInterface; use Illuminate\Database\ConnectionInterface;
use Illuminate\Support\Facades\Notification; use Illuminate\Support\Facades\Notification;
use Illuminate\Contracts\Auth\PasswordBroker;
use Pterodactyl\Notifications\AccountCreated; use Pterodactyl\Notifications\AccountCreated;
use Pterodactyl\Services\Users\UserCreationService; use Pterodactyl\Services\Users\UserCreationService;
use Pterodactyl\Services\Helpers\TemporaryPasswordService;
use Pterodactyl\Contracts\Repository\UserRepositoryInterface; use Pterodactyl\Contracts\Repository\UserRepositoryInterface;
class UserCreationServiceTest extends TestCase class UserCreationServiceTest extends TestCase
@ -29,9 +29,9 @@ class UserCreationServiceTest extends TestCase
private $hasher; private $hasher;
/** /**
* @var \Pterodactyl\Services\Helpers\TemporaryPasswordService|\Mockery\Mock * @var \Illuminate\Contracts\Auth\PasswordBroker|\Mockery\Mock
*/ */
private $passwordService; private $passwordBroker;
/** /**
* @var \Pterodactyl\Contracts\Repository\UserRepositoryInterface|\Mockery\Mock * @var \Pterodactyl\Contracts\Repository\UserRepositoryInterface|\Mockery\Mock
@ -48,7 +48,7 @@ class UserCreationServiceTest extends TestCase
Notification::fake(); Notification::fake();
$this->connection = m::mock(ConnectionInterface::class); $this->connection = m::mock(ConnectionInterface::class);
$this->hasher = m::mock(Hasher::class); $this->hasher = m::mock(Hasher::class);
$this->passwordService = m::mock(TemporaryPasswordService::class); $this->passwordBroker = m::mock(PasswordBroker::class);
$this->repository = m::mock(UserRepositoryInterface::class); $this->repository = m::mock(UserRepositoryInterface::class);
} }
@ -121,7 +121,7 @@ class UserCreationServiceTest extends TestCase
$this->hasher->shouldNotReceive('make'); $this->hasher->shouldNotReceive('make');
$this->connection->shouldReceive('beginTransaction')->withNoArgs()->once()->andReturnNull(); $this->connection->shouldReceive('beginTransaction')->withNoArgs()->once()->andReturnNull();
$this->hasher->shouldReceive('make')->once()->andReturn('created-enc-password'); $this->hasher->shouldReceive('make')->once()->andReturn('created-enc-password');
$this->passwordService->shouldReceive('handle')->with($user->email)->once()->andReturn('random-token'); $this->passwordBroker->shouldReceive('createToken')->with($user)->once()->andReturn('random-token');
$this->repository->shouldReceive('create')->with([ $this->repository->shouldReceive('create')->with([
'password' => 'created-enc-password', 'password' => 'created-enc-password',
@ -152,6 +152,6 @@ class UserCreationServiceTest extends TestCase
*/ */
private function getService(): UserCreationService private function getService(): UserCreationService
{ {
return new UserCreationService($this->connection, $this->hasher, $this->passwordService, $this->repository); return new UserCreationService($this->connection, $this->hasher, $this->passwordBroker, $this->repository);
} }
} }