This commit is contained in:
Dane Everitt 2017-12-30 20:25:04 -06:00
parent 10e2e6e379
commit 5efee34378
No known key found for this signature in database
GPG key ID: EEA66103B3D71F53
11 changed files with 111 additions and 135 deletions

View file

@ -7,6 +7,7 @@ This project follows [Semantic Versioning](http://semver.org) guidelines.
### Fixed ### Fixed
* `[beta.3]` — Fixes a bug with the default environment file that was causing an inability to perform a fresh install when running package discovery. * `[beta.3]` — Fixes a bug with the default environment file that was causing an inability to perform a fresh install when running package discovery.
* `[beta.3]` — Fixes an edge case caused by the Laravel 5.5 upgrade that would try to perform an in_array check aganist a null value. * `[beta.3]` — Fixes an edge case caused by the Laravel 5.5 upgrade that would try to perform an in_array check aganist a null value.
* `[beta.3]` — Fixes a bug that would cause an error when attempting to create a new user on the Panel.
## v0.7.0-beta.3 (Derelict Dermodactylus) ## v0.7.0-beta.3 (Derelict Dermodactylus)
### Fixed ### Fixed

View file

@ -9,6 +9,7 @@ use Pterodactyl\Exceptions\DisplayException;
use Pterodactyl\Http\Controllers\Controller; use Pterodactyl\Http\Controllers\Controller;
use Illuminate\Contracts\Translation\Translator; use Illuminate\Contracts\Translation\Translator;
use Pterodactyl\Services\Users\UserUpdateService; use Pterodactyl\Services\Users\UserUpdateService;
use Pterodactyl\Traits\Helpers\AvailableLanguages;
use Pterodactyl\Services\Users\UserCreationService; use Pterodactyl\Services\Users\UserCreationService;
use Pterodactyl\Services\Users\UserDeletionService; use Pterodactyl\Services\Users\UserDeletionService;
use Pterodactyl\Http\Requests\Admin\UserFormRequest; use Pterodactyl\Http\Requests\Admin\UserFormRequest;
@ -16,6 +17,8 @@ use Pterodactyl\Contracts\Repository\UserRepositoryInterface;
class UserController extends Controller class UserController extends Controller
{ {
use AvailableLanguages;
/** /**
* @var \Prologue\Alerts\AlertsMessageBag * @var \Prologue\Alerts\AlertsMessageBag
*/ */
@ -92,7 +95,9 @@ class UserController extends Controller
*/ */
public function create() public function create()
{ {
return view('admin.users.new'); return view('admin.users.new', [
'languages' => $this->getAvailableLanguages(true),
]);
} }
/** /**
@ -103,7 +108,10 @@ class UserController extends Controller
*/ */
public function view(User $user) public function view(User $user)
{ {
return view('admin.users.view', ['user' => $user]); return view('admin.users.view', [
'user' => $user,
'languages' => $this->getAvailableLanguages(true),
]);
} }
/** /**

View file

@ -1,11 +1,4 @@
<?php <?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\Http\Requests\Admin; namespace Pterodactyl\Http\Requests\Admin;
@ -39,11 +32,11 @@ abstract class AdminFormRequest extends FormRequest
* Return only the fields that we are interested in from the request. * Return only the fields that we are interested in from the request.
* This will include empty fields as a null value. * This will include empty fields as a null value.
* *
* @param array $only * @param array|null $only
* @return array * @return array
*/ */
public function normalize($only = []) public function normalize(array $only = null)
{ {
return $this->all(empty($only) ? array_keys($this->rules()) : $only); return $this->only($only ?? array_keys($this->rules()));
} }
} }

View file

@ -22,12 +22,16 @@ class UserFormRequest extends AdminFormRequest
return User::getCreateRules(); return User::getCreateRules();
} }
public function normalize($only = []) /**
* @param array|null $only
* @return array
*/
public function normalize(array $only = null)
{ {
if ($this->method === 'PATCH') { if ($this->method === 'PATCH') {
return array_merge( return array_merge(
$this->all(['password']), $this->all(['password']),
$this->only(['email', 'username', 'name_first', 'name_last', 'root_admin', 'ignore_connection_error']) $this->only(['email', 'username', 'name_first', 'name_last', 'root_admin', 'language', 'ignore_connection_error'])
); );
} }

View file

@ -1,11 +1,4 @@
<?php <?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\Models; namespace Pterodactyl\Models;
@ -127,6 +120,8 @@ class User extends Model implements
'name_first' => 'required', 'name_first' => 'required',
'name_last' => 'required', 'name_last' => 'required',
'password' => 'sometimes', 'password' => 'sometimes',
'language' => 'sometimes',
'use_totp' => 'sometimes',
]; ];
/** /**

View file

@ -1,27 +1,14 @@
<?php <?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\Observers; namespace Pterodactyl\Observers;
use Pterodactyl\Events; use Pterodactyl\Events;
use Pterodactyl\Models\User; use Pterodactyl\Models\User;
use Pterodactyl\Services\Components\UuidService;
class UserObserver class UserObserver
{ {
protected $uuid; protected $uuid;
public function __construct(UuidService $uuid)
{
$this->uuid = $uuid;
}
/** /**
* Listen to the User creating event. * Listen to the User creating event.
* *
@ -29,8 +16,6 @@ class UserObserver
*/ */
public function creating(User $user) public function creating(User $user)
{ {
$user->uuid = $this->uuid->generate('users', 'uuid');
event(new Events\User\Creating($user)); event(new Events\User\Creating($user));
} }

View file

@ -1,63 +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\Components;
use DB;
use Uuid;
class UuidService
{
/**
* Generate a unique UUID validating against specified table and column.
* Defaults to `users.uuid`.
*
* @param string $table
* @param string $field
* @param int $type
* @return string
* @deprecated
*/
public function generate($table = 'users', $field = 'uuid', $type = 4)
{
$return = false;
do {
$uuid = Uuid::generate($type);
if (! DB::table($table)->where($field, $uuid)->exists()) {
$return = $uuid;
}
} while (! $return);
return (string) $return;
}
/**
* Generates a ShortUUID code which is 8 characters long and is used for identifying servers in the system.
*
* @param string $table
* @param string $field
* @param null|string $attachedUuid
* @return string
* @deprecated
*/
public function generateShort($table = 'servers', $field = 'uuidShort', $attachedUuid = null)
{
$return = false;
do {
$short = (is_null($attachedUuid)) ? substr(Uuid::generate(4), 0, 8) : substr($attachedUuid, 0, 8);
$attachedUuid = null;
if (! DB::table($table)->where($field, $short)->exists()) {
$return = $short;
}
} while (! $return);
return (string) $return;
}
}

View file

@ -9,6 +9,7 @@
namespace Pterodactyl\Services\Users; namespace Pterodactyl\Services\Users;
use Ramsey\Uuid\Uuid;
use Illuminate\Foundation\Application; use Illuminate\Foundation\Application;
use Illuminate\Contracts\Hashing\Hasher; use Illuminate\Contracts\Hashing\Hasher;
use Illuminate\Database\ConnectionInterface; use Illuminate\Database\ConnectionInterface;
@ -96,7 +97,10 @@ class UserCreationService
$token = $this->passwordService->handle($data['email']); $token = $this->passwordService->handle($data['email']);
} }
$user = $this->repository->create($data); $user = $this->repository->create(array_merge($data, [
'uuid' => Uuid::uuid4()->toString(),
]));
$this->connection->commit(); $this->connection->commit();
// @todo fire event, handle notification there // @todo fire event, handle notification there

View file

@ -51,6 +51,17 @@
<input type="text" autocomplete="off" name="name_last" value="{{ old('name_last') }}" class="form-control" /> <input type="text" autocomplete="off" name="name_last" value="{{ old('name_last') }}" class="form-control" />
</div> </div>
</div> </div>
<div class="form-group">
<label class="control-label">Default Langauge</label>
<div>
<select name="language" class="form-control">
@foreach($languages as $key => $value)
<option value="{{ $key }}" @if(config('app.locale') === $key) selected @endif>{{ $value }}</option>
@endforeach
</select>
<p class="text-muted"><small>The default language to use when rendering the Panel for this user.</small></p>
</div>
</div>
</div> </div>
<div class="box-footer"> <div class="box-footer">
{!! csrf_field() !!} {!! csrf_field() !!}

View file

@ -51,6 +51,17 @@
<input readonly type="text" name="name_last" value="{{ $user->name_last }}" class="form-control form-autocomplete-stop"> <input readonly type="text" name="name_last" value="{{ $user->name_last }}" class="form-control form-autocomplete-stop">
</div> </div>
</div> </div>
<div class="form-group">
<label class="control-label">Default Langauge</label>
<div>
<select name="language" class="form-control">
@foreach($languages as $key => $value)
<option value="{{ $key }}" @if($user->language === $key) selected @endif>{{ $value }}</option>
@endforeach
</select>
<p class="text-muted"><small>The default language to use when rendering the Panel for this user.</small></p>
</div>
</div>
</div> </div>
<div class="box-footer"> <div class="box-footer">
{!! csrf_field() !!} {!! csrf_field() !!}
@ -101,25 +112,24 @@
</div> </div>
</div> </div>
</form> </form>
<div class="col-xs-12"> {{--<div class="col-xs-12">--}}
<div class="box"> {{--<div class="box">--}}
<div class="box-header with-border"> {{--<div class="box-header with-border">--}}
<h3 class="box-title">Associated Servers</h3> {{--<h3 class="box-title">Associated Servers</h3>--}}
</div> {{--</div>--}}
<div class="box-body table-responsive no-padding"> {{--<div class="box-body table-responsive no-padding">--}}
<table class="table table-hover"> {{--<table class="table table-hover">--}}
<thead> {{--<thead>--}}
<tr> {{--<tr>--}}
<th style="width:2%;"></th> {{--<th style="width:2%;"></th>--}}
<th>Identifier</th> {{--<th>Identifier</th>--}}
<th>Server Name</th> {{--<th>Server Name</th>--}}
<th>Access</th> {{--<th>Access</th>--}}
<th>Node</th> {{--<th>Node</th>--}}
<th style="width:10%;"></th> {{--<th style="width:10%;"></th>--}}
</tr> {{--</tr>--}}
</thead> {{--</thead>--}}
<tbody> {{--<tbody>--}}
Oh dear, this hasn't been fixed yet?
{{--@foreach($user->setAccessLevel('subuser')->access()->get() as $server)--}} {{--@foreach($user->setAccessLevel('subuser')->access()->get() as $server)--}}
{{--<tr>--}} {{--<tr>--}}
{{--<td><a href="{{ route('server.index', $server->uuidShort) }}/"><i class="fa fa-tachometer"></i></a></td>--}} {{--<td><a href="{{ route('server.index', $server->uuidShort) }}/"><i class="fa fa-tachometer"></i></a></td>--}}
@ -136,12 +146,11 @@
{{--<td class="centered">@if($server->suspended === 0)<span class="label muted muted-hover label-success">Active</span>@else<span class="label label-warning">Suspended</span>@endif</td>--}} {{--<td class="centered">@if($server->suspended === 0)<span class="label muted muted-hover label-success">Active</span>@else<span class="label label-warning">Suspended</span>@endif</td>--}}
{{--</td>--}} {{--</td>--}}
{{--@endforeach--}} {{--@endforeach--}}
</tbody> {{--</tbody>--}}
</table> {{--</table>--}}
</div> {{--</div>--}}
</form> {{--</div>--}}
</div> {{--</div>--}}
</div>
<div class="col-xs-12"> <div class="col-xs-12">
<div class="box box-danger"> <div class="box box-danger">
<div class="box-header with-border"> <div class="box-header with-border">

View file

@ -1,16 +1,10 @@
<?php <?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 Tests\Unit\Services; namespace Tests\Unit\Services;
use Mockery as m; use Mockery as m;
use Tests\TestCase; use Tests\TestCase;
use Tests\Traits\MocksUuids;
use Illuminate\Foundation\Application; use Illuminate\Foundation\Application;
use Illuminate\Contracts\Hashing\Hasher; use Illuminate\Contracts\Hashing\Hasher;
use Illuminate\Database\ConnectionInterface; use Illuminate\Database\ConnectionInterface;
@ -22,6 +16,8 @@ use Pterodactyl\Contracts\Repository\UserRepositoryInterface;
class UserCreationServiceTest extends TestCase class UserCreationServiceTest extends TestCase
{ {
use MocksUuids;
/** /**
* @var \Illuminate\Foundation\Application * @var \Illuminate\Foundation\Application
*/ */
@ -93,9 +89,10 @@ class UserCreationServiceTest extends TestCase
$this->hasher->shouldReceive('make')->with('raw-password')->once()->andReturn('enc-password'); $this->hasher->shouldReceive('make')->with('raw-password')->once()->andReturn('enc-password');
$this->database->shouldReceive('beginTransaction')->withNoArgs()->once()->andReturnNull(); $this->database->shouldReceive('beginTransaction')->withNoArgs()->once()->andReturnNull();
$this->hasher->shouldNotReceive('make'); $this->repository->shouldReceive('create')->with([
$this->passwordService->shouldNotReceive('handle'); 'password' => 'enc-password',
$this->repository->shouldReceive('create')->with(['password' => 'enc-password'])->once()->andReturn($user); 'uuid' => $this->getKnownUuid(),
])->once()->andReturn($user);
$this->database->shouldReceive('commit')->withNoArgs()->once()->andReturnNull(); $this->database->shouldReceive('commit')->withNoArgs()->once()->andReturnNull();
$this->appMock->shouldReceive('makeWith')->with(AccountCreated::class, [ $this->appMock->shouldReceive('makeWith')->with(AccountCreated::class, [
'user' => [ 'user' => [
@ -116,6 +113,37 @@ class UserCreationServiceTest extends TestCase
$this->assertEquals($user->name_first, 'FirstName'); $this->assertEquals($user->name_first, 'FirstName');
} }
/**
* Test that a UUID passed in the submission data is not used when
* creating the user.
*/
public function testUuidPassedInDataIsIgnored()
{
$user = (object) [
'name_first' => 'FirstName',
'username' => 'user_name',
];
$this->hasher->shouldReceive('make')->andReturn('enc-password');
$this->database->shouldReceive('beginTransaction')->andReturnNull();
$this->repository->shouldReceive('create')->with([
'password' => 'enc-password',
'uuid' => $this->getKnownUuid(),
])->once()->andReturn($user);
$this->database->shouldReceive('commit')->andReturnNull();
$this->appMock->shouldReceive('makeWith')->andReturnNull();
$this->notification->shouldReceive('send')->andReturnNull();
$response = $this->service->handle([
'password' => 'raw-password',
'uuid' => 'test-uuid',
]);
$this->assertNotNull($response);
$this->assertEquals($user->username, $response->username);
$this->assertEquals($user->name_first, 'FirstName');
}
/** /**
* Test that a user is created with a random password when no password is provided. * Test that a user is created with a random password when no password is provided.
*/ */
@ -138,6 +166,7 @@ class UserCreationServiceTest extends TestCase
$this->repository->shouldReceive('create')->with([ $this->repository->shouldReceive('create')->with([
'password' => 'created-enc-password', 'password' => 'created-enc-password',
'email' => 'user@example.com', 'email' => 'user@example.com',
'uuid' => $this->getKnownUuid(),
])->once()->andReturn($user); ])->once()->andReturn($user);
$this->database->shouldReceive('commit')->withNoArgs()->once()->andReturnNull(); $this->database->shouldReceive('commit')->withNoArgs()->once()->andReturnNull();