forked from apiato/apiato
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat!(password): separate
update password
functionality from `updat…
…e user` functionality
- Loading branch information
1 parent
0cb1058
commit d3a57a2
Showing
11 changed files
with
324 additions
and
49 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
31 changes: 31 additions & 0 deletions
31
app/Containers/AppSection/User/Actions/UpdateUserPasswordAction.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
<?php | ||
|
||
namespace App\Containers\AppSection\User\Actions; | ||
|
||
use Apiato\Core\Exceptions\IncorrectIdException; | ||
use App\Containers\AppSection\User\Models\User; | ||
use App\Containers\AppSection\User\Tasks\UpdateUserTask; | ||
use App\Containers\AppSection\User\UI\API\Requests\UpdateUserPasswordRequest; | ||
use App\Ship\Exceptions\NotFoundException; | ||
use App\Ship\Exceptions\UpdateResourceFailedException; | ||
use App\Ship\Parents\Actions\Action; | ||
|
||
class UpdateUserPasswordAction extends Action | ||
{ | ||
/** | ||
* @param UpdateUserPasswordRequest $request | ||
* @return User | ||
* @throws IncorrectIdException | ||
* @throws NotFoundException | ||
* @throws UpdateResourceFailedException | ||
*/ | ||
public function run(UpdateUserPasswordRequest $request): User | ||
{ | ||
$sanitizedData = $request->sanitizeInput([ | ||
'current_password', | ||
'new_password', | ||
]); | ||
|
||
return app(UpdateUserTask::class)->run(['password' => $sanitizedData['new_password']], $request->id); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
30 changes: 30 additions & 0 deletions
30
app/Containers/AppSection/User/UI/API/Controllers/UpdateUserPasswordController.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
<?php | ||
|
||
namespace App\Containers\AppSection\User\UI\API\Controllers; | ||
|
||
use Apiato\Core\Exceptions\IncorrectIdException; | ||
use Apiato\Core\Exceptions\InvalidTransformerException; | ||
use App\Containers\AppSection\User\Actions\UpdateUserPasswordAction; | ||
use App\Containers\AppSection\User\UI\API\Requests\UpdateUserPasswordRequest; | ||
use App\Containers\AppSection\User\UI\API\Transformers\UserTransformer; | ||
use App\Ship\Exceptions\NotFoundException; | ||
use App\Ship\Exceptions\UpdateResourceFailedException; | ||
use App\Ship\Parents\Controllers\ApiController; | ||
|
||
class UpdateUserPasswordController extends ApiController | ||
{ | ||
/** | ||
* @param UpdateUserPasswordRequest $request | ||
* @return array | ||
* @throws IncorrectIdException | ||
* @throws InvalidTransformerException | ||
* @throws NotFoundException | ||
* @throws UpdateResourceFailedException | ||
*/ | ||
public function updateUserPassword(UpdateUserPasswordRequest $request): array | ||
{ | ||
$user = app(UpdateUserPasswordAction::class)->run($request); | ||
|
||
return $this->transform($user, UserTransformer::class); | ||
} | ||
} |
56 changes: 56 additions & 0 deletions
56
app/Containers/AppSection/User/UI/API/Requests/UpdateUserPasswordRequest.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
<?php | ||
|
||
namespace App\Containers\AppSection\User\UI\API\Requests; | ||
|
||
use App\Containers\AppSection\Authorization\Traits\IsResourceOwnerTrait; | ||
use App\Containers\AppSection\User\Models\User; | ||
use App\Ship\Parents\Requests\Request; | ||
use Illuminate\Validation\Rule; | ||
|
||
class UpdateUserPasswordRequest extends Request | ||
{ | ||
use IsResourceOwnerTrait; | ||
|
||
/** | ||
* Define which Roles and/or Permissions has access to this request. | ||
*/ | ||
protected array $access = [ | ||
'permissions' => '', | ||
'roles' => '', | ||
]; | ||
|
||
/** | ||
* Id's that needs decoding before applying the validation rules. | ||
*/ | ||
protected array $decode = [ | ||
'id', | ||
]; | ||
|
||
/** | ||
* Defining the URL parameters (`/stores/999/items`) allows applying | ||
* validation rules on them and allows accessing them like request data. | ||
*/ | ||
protected array $urlParameters = [ | ||
'id', | ||
]; | ||
|
||
public function rules(): array | ||
{ | ||
return [ | ||
'current_password' => [Rule::requiredIf( | ||
fn (): bool => !is_null($this->user()->password) | ||
), 'current_password:api'], | ||
'new_password' => [ | ||
'required', | ||
User::getPasswordValidationRules(), | ||
], | ||
]; | ||
} | ||
|
||
public function authorize(): bool | ||
{ | ||
return $this->check([ | ||
'hasAccess|isResourceOwner', | ||
]); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
31 changes: 31 additions & 0 deletions
31
app/Containers/AppSection/User/UI/API/Routes/UpdateUserPassword.v1.private.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
<?php | ||
|
||
/** | ||
* @apiGroup User | ||
* @apiName UpdateUserPassword | ||
* @api {patch} /v1/users/:id Update User's Password | ||
* | ||
* @apiVersion 1.0.0 | ||
* @apiPermission Authenticated ['permissions' => 'update-users', 'roles' => ''] | Resource Owner | ||
* | ||
* @apiParam {String} current_password | ||
* @apiParam {String} new_password min: 8 | ||
* | ||
* at least one character of the following: | ||
* | ||
* upper case letter | ||
* | ||
* lower case letter | ||
* | ||
* number | ||
* | ||
* special character | ||
* | ||
* @apiUse UserSuccessSingleResponse | ||
*/ | ||
|
||
use App\Containers\AppSection\User\UI\API\Controllers\UpdateUserPasswordController; | ||
use Illuminate\Support\Facades\Route; | ||
|
||
Route::patch('users/{id}/password', [UpdateUserPasswordController::class, 'updateUserPassword']) | ||
->middleware(['auth:api']); |
120 changes: 120 additions & 0 deletions
120
app/Containers/AppSection/User/UI/API/Tests/Functional/UpdateUserPasswordTest.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
<?php | ||
|
||
namespace App\Containers\AppSection\User\UI\API\Tests\Functional; | ||
|
||
use App\Containers\AppSection\User\UI\API\Tests\ApiTestCase; | ||
use Illuminate\Testing\Fluent\AssertableJson; | ||
|
||
/** | ||
* Class UpdateUserPasswordTest. | ||
* | ||
* @group user | ||
* @group api | ||
*/ | ||
class UpdateUserPasswordTest extends ApiTestCase | ||
{ | ||
protected string $endpoint = 'patch@v1/users/{id}/password'; | ||
|
||
protected array $access = [ | ||
'permissions' => '', | ||
'roles' => '', | ||
]; | ||
|
||
public function testGivenUserAlreadyHaveAPassword_UpdateUserPassword(): void | ||
{ | ||
$user = $this->getTestingUser([ | ||
'password' => 'Av@dakedavra!', | ||
]); | ||
$data = [ | ||
'current_password' => 'Av@dakedavra!', | ||
'new_password' => 'updated#Password111', | ||
]; | ||
|
||
$response = $this->injectId($user->id)->makeCall($data); | ||
|
||
$response->assertStatus(200); | ||
$response->assertJson( | ||
fn (AssertableJson $json) => $json->has('data') | ||
->where('data.object', 'User') | ||
->where('data.email', $user->email) | ||
->missing('data.password') | ||
->etc() | ||
); | ||
} | ||
|
||
public function testNewPasswordFieldShouldBeRequired(): void | ||
{ | ||
$user = $this->getTestingUser(); | ||
$data = [ | ||
'new_password' => '', | ||
]; | ||
|
||
$response = $this->injectId($user->id)->makeCall($data); | ||
|
||
$response->assertStatus(422); | ||
$response->assertJson( | ||
fn (AssertableJson $json) => $json->has('errors') | ||
->where('errors.new_password.0', 'The new password field is required.') | ||
->etc() | ||
); | ||
} | ||
|
||
public function testGivenUserAlreadyHaveAPassword_CurrentPasswordFieldShouldBeRequired(): void | ||
{ | ||
$user = $this->getTestingUser([ | ||
'password' => 'Av@dakedavra!', | ||
]); | ||
$data = [ | ||
'new_password' => 'updated#Password111', | ||
]; | ||
|
||
$response = $this->injectId($user->id)->makeCall($data); | ||
|
||
$response->assertStatus(422); | ||
$response->assertJson( | ||
fn (AssertableJson $json) => $json->has('errors') | ||
->where('errors.current_password.0', 'The current password field is required.') | ||
->etc() | ||
); | ||
} | ||
|
||
public function testGivenUserAlreadyHaveAPassword_CurrentPasswordFieldMustMatchUserCurrentPassword(): void | ||
{ | ||
$user = $this->getTestingUser([ | ||
'password' => 'Av@dakedavra!', | ||
]); | ||
$data = [ | ||
'current_password' => 'notMatchingP@ssw0rd', | ||
'new_password' => 'updated#Password111', | ||
]; | ||
|
||
$response = $this->injectId($user->id)->makeCall($data); | ||
|
||
$response->assertStatus(422); | ||
$response->assertJson( | ||
fn (AssertableJson $json) => $json->has('errors') | ||
->where('errors.current_password.0', 'The password is incorrect.') | ||
->etc() | ||
); | ||
} | ||
|
||
public function testGivenUserDoesntHaveAPassword_CurrentPasswordFieldShouldBeProhibited(): void | ||
{ | ||
$user = $this->getTestingUser([ | ||
'password' => null, | ||
]); | ||
$data = [ | ||
'current_password' => 'sh0uldBeProhibited!!11', | ||
'new_password' => 'updated#Password111', | ||
]; | ||
|
||
$response = $this->injectId($user->id)->makeCall($data); | ||
|
||
$response->assertStatus(422); | ||
$response->assertJson( | ||
fn (AssertableJson $json) => $json->has('errors') | ||
->where('errors.current_password.0', 'The password is incorrect.') | ||
->etc() | ||
); | ||
} | ||
} |
Oops, something went wrong.