File size: 7,221 Bytes
d2897cd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
<?php

declare(strict_types=1);

namespace Mautic\UserBundle\Tests\Controller\Api;

use Mautic\CoreBundle\Test\MauticMysqlTestCase;
use Mautic\UserBundle\Entity\Permission;
use Mautic\UserBundle\Entity\Role;
use Mautic\UserBundle\Entity\User;
use PHPUnit\Framework\Assert;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

class UserApiControllerFunctionalTest extends MauticMysqlTestCase
{
    public function testRoleUpdateByApiGivesErrorResponseIfUserDoesNotExist(): void
    {
        // Assuming user with id 99999 does not exist
        $this->client->request(Request::METHOD_PATCH, '/api/users/99999/edit', ['role' => 1]);
        $clientResponse = $this->client->getResponse();
        Assert::assertSame(Response::HTTP_NOT_FOUND, $clientResponse->getStatusCode());
        Assert::assertStringContainsString('"message":"Item was not found."', $clientResponse->getContent());
    }

    public function testRoleUpdateByApiGivesErrorResponseIfRoleDoesNotExist(): void
    {
        // Assuming role with id 99999 does not exist
        $this->client->request(Request::METHOD_PATCH, '/api/users/1/edit', ['role' => 99999]);
        $clientResponse = $this->client->getResponse();
        Assert::assertSame(Response::HTTP_BAD_REQUEST, $clientResponse->getStatusCode());
        Assert::assertStringContainsString('"message":"role: This value is not valid."', $clientResponse->getContent());
    }

    public function testRoleUpdateByApiGivesErrorResponseWithInvalidRequestFormat(): void
    {
        // Correct request format is ['role' => 2]
        $this->client->request(Request::METHOD_PATCH, '/api/users/1/edit', ['role' => ['id' => 2]]);
        $clientResponse = $this->client->getResponse();
        Assert::assertSame(Response::HTTP_BAD_REQUEST, $clientResponse->getStatusCode());
        Assert::assertStringContainsString('"message":"role: This value is not valid."', $clientResponse->getContent());
    }

    public function testRoleUpdateByApiGivesErrorResponseIfUserDoesNotHaveValidPermissionToUpdate(): void
    {
        // Create non-admin role
        $role = $this->createRole();
        // Create permissions for the role
        $this->createPermission('lead:leads:viewown', $role, 1024);
        // Create non-admin user
        $user = $this->createUser($role);
        $this->em->flush();
        $this->em->clear();

        // Login newly created non-admin user
        $this->loginUser($user->getUserIdentifier());
        $this->client->setServerParameter('PHP_AUTH_USER', $user->getUserIdentifier());
        $this->client->setServerParameter('PHP_AUTH_PW', 'mautic');

        $this->client->request(Request::METHOD_PATCH, "/api/users/{$user->getId()}/edit", ['role' => $role->getId()]);
        $clientResponse = $this->client->getResponse();
        Assert::assertSame(Response::HTTP_FORBIDDEN, $clientResponse->getStatusCode());
        Assert::assertStringContainsString(
            '"message":"You do not have access to the requested area\/action."',
            $clientResponse->getContent()
        );
    }

    public function testRoleUpdateByApiThroughAdminUserGivesSuccessResponse(): void
    {
        // Create admin role
        $role = $this->createRole(true);
        // Create admin user
        $user = $this->createUser($role);
        $this->em->flush();
        $this->em->clear();

        // Login newly created admin user
        $this->loginUser($user->getUserIdentifier());
        $this->client->setServerParameter('PHP_AUTH_USER', $user->getUserIdentifier());
        $this->client->setServerParameter('PHP_AUTH_PW', 'mautic');

        $this->client->request(Request::METHOD_PATCH, "/api/users/{$user->getId()}/edit", ['role' => $role->getId()]);
        $clientResponse = $this->client->getResponse();
        Assert::assertSame(Response::HTTP_OK, $clientResponse->getStatusCode());
        Assert::assertStringContainsString('"username":"'.$user->getUserIdentifier().'"', $clientResponse->getContent());
    }

    public function testRoleUpdateByApiThroughNonAdminUserGivesSuccessResponse(): void
    {
        // Create non-admin role
        $role = $this->createRole();
        // Create permissions to update user for the role
        $this->createPermission('user:users:edit', $role, 52);
        // Create non-admin user
        $user = $this->createUser($role);
        $this->em->flush();
        $this->em->clear();

        $this->loginUser($user->getUserIdentifier());
        $this->client->setServerParameter('PHP_AUTH_USER', $user->getUserIdentifier());
        $this->client->setServerParameter('PHP_AUTH_PW', 'mautic');

        $this->client->request(Request::METHOD_PATCH, "/api/users/{$user->getId()}/edit", ['role' => $role->getId()]);
        $clientResponse = $this->client->getResponse();
        Assert::assertSame(Response::HTTP_OK, $clientResponse->getStatusCode());
        Assert::assertStringContainsString('"username":"'.$user->getUserIdentifier().'"', $clientResponse->getContent());
    }

    /**
     * @dataProvider passwordProvider
     */
    public function testUserPasswordPolicy(int $responseCode, string $password): void
    {
        $userPayload = [
            'username'      => 'lorem_ipsum',
            'firstName'     => 'lorem',
            'lastName'      => 'ipsum',
            'email'         => '[email protected]',
            'plainPassword' => ['password' => $password, 'confirm' => $password],
            'role'          => 1,
        ];

        $this->client->request(Request::METHOD_POST, '/api/users/new', $userPayload);
        $clientResponse = $this->client->getResponse();
        Assert::assertSame($responseCode, $clientResponse->getStatusCode());
    }

    /**
     * @return iterable<array<int, mixed>>
     */
    public function passwordProvider(): iterable
    {
        yield [Response::HTTP_BAD_REQUEST, 'aaa'];
        yield [Response::HTTP_BAD_REQUEST, 'qwerty'];
        yield [Response::HTTP_BAD_REQUEST, 'qwerty123'];
        yield [Response::HTTP_CREATED, 'Qwertee@123'];
    }

    private function createRole(bool $isAdmin = false): Role
    {
        $role = new Role();
        $role->setName('Role');
        $role->setIsAdmin($isAdmin);
        $this->em->persist($role);

        return $role;
    }

    private function createPermission(string $rawPermission, Role $role, int $bitwise): void
    {
        $parts      = explode(':', $rawPermission);
        $permission = new Permission();
        $permission->setBundle($parts[0]);
        $permission->setName($parts[1]);
        $permission->setRole($role);
        $permission->setBitwise($bitwise);
        $this->em->persist($permission);
    }

    private function createUser(Role $role): User
    {
        $user = new User();
        $user->setFirstName('John');
        $user->setLastName('Doe');
        $user->setUsername('john.doe');
        $user->setEmail('[email protected]');
        $encoder = static::getContainer()->get('security.encoder_factory')->getEncoder($user);
        $user->setPassword($encoder->encodePassword('mautic', null));
        $user->setRole($role);
        $this->em->persist($user);

        return $user;
    }
}