Skip to content

Commit 0cc895f

Browse files
committed
Finalize email/password changing in UI
1 parent 81da55d commit 0cc895f

File tree

10 files changed

+158
-23
lines changed

10 files changed

+158
-23
lines changed

app/Http/Controllers/Api/Client/AccountController.php

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@
33
namespace Pterodactyl\Http\Controllers\Api\Client;
44

55
use Illuminate\Http\Request;
6+
use Illuminate\Http\Response;
67
use Pterodactyl\Services\Users\UserUpdateService;
78
use Pterodactyl\Transformers\Api\Client\AccountTransformer;
89
use Pterodactyl\Http\Requests\Api\Client\Account\UpdateEmailRequest;
10+
use Pterodactyl\Http\Requests\Api\Client\Account\UpdatePasswordRequest;
911

1012
class AccountController extends ClientApiController
1113
{
@@ -38,20 +40,34 @@ public function index(Request $request): array
3840
}
3941

4042
/**
41-
* Update the authenticated user's email address if their password matches.
43+
* Update the authenticated user's email address.
4244
*
4345
* @param \Pterodactyl\Http\Requests\Api\Client\Account\UpdateEmailRequest $request
44-
* @return array
46+
* @return \Illuminate\Http\Response
4547
*
4648
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
4749
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
4850
*/
49-
public function updateEmail(UpdateEmailRequest $request): array
51+
public function updateEmail(UpdateEmailRequest $request): Response
5052
{
51-
$updated = $this->updateService->handle($request->user(), $request->validated());
53+
$this->updateService->handle($request->user(), $request->validated());
5254

53-
return $this->fractal->item($updated->get('model'))
54-
->transformWith($this->getTransformer(AccountTransformer::class))
55-
->toArray();
55+
return response('', Response::HTTP_CREATED);
56+
}
57+
58+
/**
59+
* Update the authenticated user's password.
60+
*
61+
* @param \Pterodactyl\Http\Requests\Api\Client\Account\UpdatePasswordRequest $request
62+
* @return \Illuminate\Http\Response
63+
*
64+
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
65+
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
66+
*/
67+
public function updatePassword(UpdatePasswordRequest $request): Response
68+
{
69+
$this->updateService->handle($request->user(), $request->validated());
70+
71+
return response('', Response::HTTP_CREATED);
5672
}
5773
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?php
2+
3+
namespace Pterodactyl\Http\Requests\Api\Client\Account;
4+
5+
use Pterodactyl\Models\User;
6+
use Pterodactyl\Http\Requests\Api\Client\ClientApiRequest;
7+
use Pterodactyl\Exceptions\Http\Base\InvalidPasswordProvidedException;
8+
9+
class UpdatePasswordRequest extends ClientApiRequest
10+
{
11+
/**
12+
* @return bool
13+
*
14+
* @throws \Pterodactyl\Exceptions\Http\Base\InvalidPasswordProvidedException
15+
*/
16+
public function authorize(): bool
17+
{
18+
if (! parent::authorize()) {
19+
return false;
20+
}
21+
22+
// Verify password matches when changing password or email.
23+
if (! password_verify($this->input('current_password'), $this->user()->password)) {
24+
throw new InvalidPasswordProvidedException(trans('base.account.invalid_password'));
25+
}
26+
27+
return true;
28+
}
29+
30+
/**
31+
* @return array
32+
*/
33+
public function rules(): array
34+
{
35+
$rules = User::getUpdateRulesForId($this->user()->id);
36+
37+
return ['password' => array_merge($rules['password'], ['confirmed'])];
38+
}
39+
}

package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
"name": "pterodactyl-panel",
33
"dependencies": {
44
"date-fns": "^1.29.0",
5+
"vee-validate": "^2.1.0-beta.2",
56
"vue": "^2.5.7",
67
"vue-axios": "^2.1.1",
78
"vue-router": "^3.0.1",
@@ -57,7 +58,8 @@
5758
"watch": "NODE_ENV=development ./node_modules/.bin/webpack --watch --progress",
5859
"build": "NODE_ENV=development ./node_modules/.bin/webpack --progress",
5960
"build:production": "NODE_ENV=production ./node_modules/.bin/webpack",
60-
"serve": "NODE_ENV=development webpack-serve --hot --config ./webpack.config.js --no-clipboard",
61-
"v:serve": "PUBLIC_PATH=http://192.168.50.2:8080 NODE_ENV=development webpack-serve --hot --config ./webpack.config.js --host 192.168.50.2 --no-clipboard"
61+
"serve": "NODE_ENV=development webpack-serve --hot --config ./webpack.config.js --no-clipboard --progress",
62+
"v:serve": "PUBLIC_PATH=http://192.168.50.2:8080 NODE_ENV=development webpack-serve --hot --config ./webpack.config.js --host 192.168.50.2 --no-clipboard",
63+
"compile:assets": "php artisan vue-i18n:generate & php artisan ziggy:generate resources/assets/scripts/helpers/ziggy.js"
6264
}
6365
}

resources/assets/scripts/app.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import Vue from 'vue';
22
import Vuex from 'vuex';
33
import vuexI18n from 'vuex-i18n';
44
import VueRouter from 'vue-router';
5+
import VeeValidate from 'vee-validate';
56

67
Vue.config.productionTip = false;
78
require('./bootstrap');
@@ -19,6 +20,7 @@ window.Ziggy = Ziggy;
1920
Vue.use(Vuex);
2021
Vue.use(VueRouter);
2122
Vue.use(vuexI18n.plugin, store);
23+
Vue.use(VeeValidate);
2224

2325
const route = require('./../../../vendor/tightenco/ziggy/src/js/route').default;
2426

resources/assets/scripts/components/dashboard/Account.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<modal :show="modalVisible" v-on:close="modalVisible = false">
66
<TwoFactorAuthentication/>
77
</modal>
8-
<flash container="mt-2 sm:mt-6 mb-2 sm:mx-4"/>
8+
<flash container="mt-2 sm:mt-6 mb-2"/>
99
<div class="flex flex-wrap">
1010
<div class="w-full md:w-1/2">
1111
<div class="sm:m-4 md:ml-0">
Lines changed: 66 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,34 @@
11
<template>
22
<div>
3-
<form action="" method="post">
3+
<form method="post" v-on:submit.prevent="submitForm">
44
<div class="content-box">
55
<h2 class="mb-6 text-grey-darkest font-medium">Change your password</h2>
66
<div class="mt-6">
77
<label for="grid-password-current" class="input-label">Current password</label>
8-
<input id="grid-password-current" name="password" type="password" class="input" required>
8+
<input id="grid-password-current" name="current_password" type="password" class="input" required
9+
ref="current"
10+
v-model="current"
11+
>
912
</div>
1013
<div class="mt-6">
1114
<label for="grid-password-new" class="input-label">New password</label>
12-
<input id="grid-password-new" name="password" type="password" class="input" required>
13-
<p class="input-help">Your new password should be at least 8 characters in length, contain one number, and be mixed case.</p>
15+
<input id="grid-password-new" name="password" type="password" class="input" required
16+
:class="{ error: errors.has('password') }"
17+
v-model="newPassword"
18+
v-validate="'min:8'"
19+
>
20+
<p class="input-help error" v-show="errors.has('password')">{{ errors.first('password') }}</p>
21+
<p class="input-help">Your new password should be at least 8 characters in length.</p>
1422
</div>
1523
<div class="mt-6">
1624
<label for="grid-password-new-confirm" class="input-label">Confirm new password</label>
17-
<input id="grid-password-new-confirm" name="password_confirmation" type="password" class="input" required>
25+
<input id="grid-password-new-confirm" name="password_confirmation" type="password" class="input" required
26+
:class="{ error: errors.has('password_confirmation') }"
27+
v-model="confirmNew"
28+
v-validate="{is: newPassword}"
29+
data-vv-as="password"
30+
>
31+
<p class="input-help error" v-show="errors.has('password_confirmation')">{{ errors.first('password_confirmation') }}</p>
1832
</div>
1933
<div class="mt-6 text-right">
2034
<button class="btn btn-blue btn-sm text-right" type="submit">Save</button>
@@ -25,7 +39,53 @@
2539
</template>
2640

2741
<script>
42+
import isObject from 'lodash/isObject';
43+
2844
export default {
29-
name: 'change-password'
45+
name: 'change-password',
46+
data: function () {
47+
return {
48+
current: '',
49+
newPassword: '',
50+
confirmNew: '',
51+
};
52+
},
53+
54+
methods: {
55+
submitForm: function () {
56+
window.axios.put(this.route('api.client.account.update-password'), {
57+
current_password: this.$data.current,
58+
password: this.$data.newPassword,
59+
password_confirmation: this.$data.confirmNew,
60+
})
61+
.finally(() => {
62+
this.clearFlashes();
63+
this.$validator.pause();
64+
this.$data.current = '';
65+
this.$refs.current.focus();
66+
})
67+
.then(() => {
68+
this.$data.newPassword = '';
69+
this.$data.confirmNew = '';
70+
71+
this.success('Your password has been updated.');
72+
})
73+
.catch(err => {
74+
if (!err.response) {
75+
return console.error(err);
76+
}
77+
78+
const response = err.response;
79+
if (response.data && isObject(response.data.errors)) {
80+
response.data.errors.forEach(error => {
81+
this.error(error.detail);
82+
});
83+
}
84+
})
85+
.finally(() => {
86+
this.$validator.resume();
87+
})
88+
}
89+
}
3090
};
3191
</script>

resources/assets/scripts/components/dashboard/account/UpdateEmail.vue

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@
66
<div>
77
<label for="grid-email" class="input-label">Email address</label>
88
<input id="grid-email" name="email" type="email" class="input" required
9-
v-model="email"
9+
:class="{ error: errors.has('email') }"
10+
v-validate
11+
v-model="email"
1012
>
11-
<p class="input-help">If your email is no longer {{ user.email }} enter a new email in the field above.</p>
13+
<p class="input-help error" v-show="errors.has('email')">{{ errors.first('email') }}</p>
1214
</div>
1315
<div class="mt-6">
1416
<label for="grid-password" class="input-label">Password</label>
@@ -25,14 +27,14 @@
2527
</template>
2628

2729
<script>
28-
import isObject from 'lodash/isObject';
30+
import { isObject, get } from 'lodash';
2931
import { mapState, mapActions } from 'vuex';
3032
3133
export default {
3234
name: 'update-email',
3335
data: function () {
3436
return {
35-
email: '',
37+
email: get(this.$store.state, 'auth.user.email', ''),
3638
password: '',
3739
};
3840
},
@@ -41,7 +43,6 @@
4143
user: state => state.auth.user,
4244
})
4345
},
44-
4546
methods: {
4647
/**
4748
* Update a user's email address on the Panel.
@@ -52,9 +53,11 @@
5253
email: this.$data.email,
5354
password: this.$data.password
5455
})
56+
.finally(() => {
57+
this.$data.password = '';
58+
})
5559
.then(() => {
5660
this.success('Your email address has been updated.');
57-
this.$data.password = '';
5861
})
5962
.catch(error => {
6063
if (!error.response) {

resources/assets/styles/components/forms.css

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,12 +49,20 @@ textarea, select, input, button {
4949
&:required, &:invalid {
5050
box-shadow: none;
5151
}
52+
53+
&.error {
54+
@apply .text-red-dark .border-red;
55+
}
5256
}
5357

5458
.input-label {
55-
@apply .block .uppercase .tracking-wide .text-grey-darkest .text-xs .font-bold .mb-1;
59+
@apply .block .uppercase .tracking-wide .text-grey-darkest .text-xs .font-bold .mb-2;
5660
}
5761

5862
.input-help {
5963
@apply .text-xs .text-grey .pt-2;
64+
65+
&.error {
66+
@apply .text-red-dark;
67+
}
6068
}

routes/api-client.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
Route::get('/', 'AccountController@index')->name('api.client.account');
1717

1818
Route::put('/email', 'AccountController@updateEmail')->name('api.client.account.update-email');
19+
Route::put('/password', 'AccountController@updatePassword')->name('api.client.account.update-password');
1920
});
2021

2122
/*

yarn.lock

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6170,6 +6170,10 @@ vary@^1.0.0:
61706170
version "1.1.2"
61716171
resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
61726172

6173+
vee-validate@^2.1.0-beta.2:
6174+
version "2.1.0-beta.2"
6175+
resolved "https://registry.yarnpkg.com/vee-validate/-/vee-validate-2.1.0-beta.2.tgz#b4a15f7aa0c4b1a9c78132d649b72a4dd4e2fa61"
6176+
61736177
vendors@^1.0.0:
61746178
version "1.0.2"
61756179
resolved "https://registry.yarnpkg.com/vendors/-/vendors-1.0.2.tgz#7fcb5eef9f5623b156bcea89ec37d63676f21801"

0 commit comments

Comments
 (0)