283 lines
11 KiB
PHP
283 lines
11 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
usesApiSuite();
|
|
|
|
const SUBUSER_PASSWORD_POLICY_MESSAGE = 'Password must contain at least one uppercase letter, one lowercase letter, and one number';
|
|
|
|
it('lists subusers when an existing grant has legacy zero permissions', function (): void {
|
|
api_test_covers('GET /subusers', 'happy');
|
|
|
|
$session = api_fixtures()->createUserSession(['list_own_subusers']);
|
|
$subuser = api_fixtures()->createSubuser([
|
|
'name' => 'Legacy Permission Driver',
|
|
]);
|
|
$grantId = api_fixtures()->grantSubuser(
|
|
$subuser['id'],
|
|
$session['user']['customer_number'],
|
|
['VEHICLES_LIST']
|
|
);
|
|
|
|
$legacyPermissions = '0';
|
|
$statement = api_test_runtime()->db()->prepare(
|
|
'UPDATE `subuser_grants` SET `permissions` = ? WHERE `id` = ?'
|
|
);
|
|
$statement->bind_param('si', $legacyPermissions, $grantId);
|
|
$statement->execute();
|
|
$statement->close();
|
|
|
|
$response = api_client()->get('/subusers?page=1&limit=5&include_non_enabled=true', $session['headers']);
|
|
|
|
$response
|
|
->assertStatus(200)
|
|
->assertEnvelope()
|
|
->assertSuccess();
|
|
|
|
$matchingSubusers = array_values(array_filter(
|
|
is_array($response->data()) ? $response->data() : [],
|
|
static fn (mixed $item): bool => is_array($item) && (int)($item['id'] ?? 0) === (int)$subuser['id']
|
|
));
|
|
|
|
expect($matchingSubusers)->toHaveCount(1);
|
|
expect($matchingSubusers[0]['grant_permissions'] ?? null)->toBe([]);
|
|
expect($matchingSubusers[0]['permissions'] ?? null)->toBe([]);
|
|
});
|
|
|
|
it('enforces the password policy for setup without rejecting legacy valid passwords at login', function (): void {
|
|
api_test_covers('POST /subusers/setup', 'failure');
|
|
api_test_covers('POST /subusers/auth/password', 'happy');
|
|
|
|
$subuser = api_fixtures()->createSubuser([
|
|
'password_plaintext' => 'invalidpassword',
|
|
]);
|
|
|
|
$setupResponse = api_client()->post('/subusers/setup', [
|
|
'token' => 'policy-test-token',
|
|
'name' => 'Policy Driver',
|
|
'password' => 'invalidpassword',
|
|
]);
|
|
|
|
$authResponse = api_client()->post('/subusers/auth/password', [
|
|
'subuser_id' => $subuser['id'],
|
|
'password' => 'invalidpassword',
|
|
]);
|
|
|
|
$setupResponse
|
|
->assertStatus(400)
|
|
->assertEnvelope()
|
|
->assertSuccess(false)
|
|
->assertMessage(SUBUSER_PASSWORD_POLICY_MESSAGE);
|
|
|
|
$authResponse
|
|
->assertStatus(200)
|
|
->assertEnvelope()
|
|
->assertSuccess();
|
|
|
|
expect($authResponse->data()['session'] ?? null)->toBeString();
|
|
(new \objects\subusers_o())->invalidateSessionToken((string)$authResponse->data()['session']);
|
|
});
|
|
|
|
it('returns a generic error for invalid subuser credentials', function (): void {
|
|
api_test_covers('POST /subusers/auth/password', 'failure');
|
|
|
|
$response = api_client()->post('/subusers/auth/password', [
|
|
'subuser_id' => 999999999,
|
|
'password' => 'whatever',
|
|
]);
|
|
|
|
$response
|
|
->assertStatus(401)
|
|
->assertEnvelope()
|
|
->assertSuccess(false)
|
|
->assertMessage('Invalid credentials');
|
|
});
|
|
|
|
it('requires subuser management access before exposing permission nodes', function (): void {
|
|
api_test_covers('GET /subusers/permission-nodes', 'auth');
|
|
|
|
$unauthenticated = api_client()->get('/subusers/permission-nodes');
|
|
$unauthenticated
|
|
->assertStatus(401)
|
|
->assertEnvelope()
|
|
->assertSuccess(false);
|
|
|
|
$session = api_fixtures()->createUserSession(['list_own_subusers']);
|
|
$authorized = api_client()->get('/subusers/permission-nodes', $session['headers']);
|
|
$authorized
|
|
->assertStatus(200)
|
|
->assertEnvelope()
|
|
->assertSuccess();
|
|
|
|
expect($authorized->data())->toBeArray()->not->toBeEmpty();
|
|
});
|
|
|
|
it('rejects customer subuser listing without own-scope permission', function (): void {
|
|
api_test_covers('GET /subusers', 'auth');
|
|
|
|
$session = api_fixtures()->createUserSession(['user']);
|
|
$response = api_client()->get('/subusers?page=1&limit=5', $session['headers']);
|
|
|
|
$response
|
|
->assertStatus(403)
|
|
->assertEnvelope()
|
|
->assertSuccess(false);
|
|
});
|
|
|
|
it('replaces older setup tokens when a new setup token is generated', function (): void {
|
|
$subuser = api_fixtures()->createSubuser([
|
|
'password_plaintext' => null,
|
|
'name' => 'Pending Setup Driver',
|
|
]);
|
|
$subuserObject = (new \objects\subusers_o())->select((int)$subuser['id']);
|
|
$subuserObject->getObjectProperties();
|
|
|
|
$firstToken = $subuserObject->generateSetupToken();
|
|
$secondToken = $subuserObject->generateSetupToken();
|
|
|
|
try {
|
|
expect((new \objects\subusers_o())->getSubuserBySetupToken($firstToken))->toBeNull();
|
|
expect((new \objects\subusers_o())->getSubuserBySetupToken($secondToken))->not->toBeNull();
|
|
} finally {
|
|
(new \objects\subusers_o())->invalidateSetupToken($secondToken);
|
|
}
|
|
});
|
|
|
|
it('validates setup identifiers before setting the driver password', function (): void {
|
|
api_test_covers('POST /subusers/setup', 'failure');
|
|
|
|
$existing = api_fixtures()->createSubuser([
|
|
'username' => 'existing-driver-setup',
|
|
]);
|
|
$pending = api_fixtures()->createSubuser([
|
|
'password_plaintext' => null,
|
|
'username' => 'pending-driver-setup',
|
|
'name' => 'Pending Driver',
|
|
]);
|
|
$pendingObject = (new \objects\subusers_o())->select((int)$pending['id']);
|
|
$pendingObject->getObjectProperties();
|
|
$token = $pendingObject->generateSetupToken();
|
|
|
|
try {
|
|
$response = api_client()->post('/subusers/setup', [
|
|
'token' => $token,
|
|
'name' => 'Pending Driver',
|
|
'username' => $existing['username'],
|
|
'password' => 'ValidPass123',
|
|
]);
|
|
|
|
$response
|
|
->assertStatus(400)
|
|
->assertEnvelope()
|
|
->assertSuccess(false)
|
|
->assertMessage('Account already exists with this username');
|
|
|
|
$row = api_fixtures()->fetchRowById('subusers', (int)$pending['id']);
|
|
expect($row['password'] ?? null)->toBeNull();
|
|
expect((new \objects\subusers_o())->getSubuserBySetupToken($token))->not->toBeNull();
|
|
} finally {
|
|
(new \objects\subusers_o())->invalidateSetupToken($token);
|
|
}
|
|
});
|
|
|
|
it('lists chauffeur grants across customers for superusers', function (): void {
|
|
$session = api_fixtures()->createUserSession(['list_subusers']);
|
|
$firstCustomer = api_fixtures()->createUser([
|
|
'display_name' => 'Fleet Customer Alpha',
|
|
'economic_customer_name' => 'Fleet Customer Alpha',
|
|
]);
|
|
$secondCustomer = api_fixtures()->createUser([
|
|
'display_name' => 'Fleet Customer Beta',
|
|
'economic_customer_name' => 'Fleet Customer Beta',
|
|
]);
|
|
$firstSubuser = api_fixtures()->createSubuser(['name' => 'Alpha Driver']);
|
|
$secondSubuser = api_fixtures()->createSubuser(['name' => 'Beta Driver']);
|
|
$firstGrantId = api_fixtures()->grantSubuser(
|
|
(int)$firstSubuser['id'],
|
|
(int)$firstCustomer['customer_number'],
|
|
['VEHICLES_LIST', 'SUBUSERS_LIST']
|
|
);
|
|
$secondGrantId = api_fixtures()->grantSubuser(
|
|
(int)$secondSubuser['id'],
|
|
(int)$secondCustomer['customer_number'],
|
|
['BOOKINGS_LIST']
|
|
);
|
|
|
|
$response = api_client()->get('/superuser/subusers?page=1&limit=20&search=Driver', $session['headers']);
|
|
|
|
$response
|
|
->assertStatus(200)
|
|
->assertEnvelope()
|
|
->assertSuccess();
|
|
|
|
$rows = array_values(array_filter(
|
|
is_array($response->data()) ? $response->data() : [],
|
|
static fn (mixed $item): bool => is_array($item)
|
|
&& in_array((int)($item['grant_id'] ?? 0), [$firstGrantId, $secondGrantId], true)
|
|
));
|
|
|
|
expect($rows)->toHaveCount(2);
|
|
|
|
$byGrantId = [];
|
|
foreach ($rows as $row) {
|
|
$byGrantId[(int)$row['grant_id']] = $row;
|
|
}
|
|
|
|
expect($byGrantId[$firstGrantId]['customer_number'])->toBe((int)$firstCustomer['customer_number']);
|
|
expect($byGrantId[$firstGrantId]['customer_name'])->toBe('Fleet Customer Alpha');
|
|
expect($byGrantId[$firstGrantId]['grant_permissions'])->toBe(['VEHICLES_LIST', 'SUBUSERS_LIST']);
|
|
expect($byGrantId[$secondGrantId]['customer_number'])->toBe((int)$secondCustomer['customer_number']);
|
|
expect($byGrantId[$secondGrantId]['customer_name'])->toBe('Fleet Customer Beta');
|
|
expect($byGrantId[$secondGrantId]['grant_permissions'])->toBe(['BOOKINGS_LIST']);
|
|
});
|
|
|
|
it('lets superusers invite chauffeurs for a selected customer', function (): void {
|
|
$session = api_fixtures()->createUserSession(['add_subusers']);
|
|
$customer = api_fixtures()->createUser([
|
|
'display_name' => 'Invite Target Customer',
|
|
'economic_customer_name' => 'Invite Target Customer',
|
|
]);
|
|
$phone = 71000000 + ((int)$customer['customer_number'] % 1000000);
|
|
$createdSubuserId = null;
|
|
$createdGrantId = null;
|
|
$setupToken = null;
|
|
|
|
try {
|
|
$response = api_client()->post('/superuser/subusers/invite', [
|
|
'customer_number' => (int)$customer['customer_number'],
|
|
'name' => 'Invited Driver',
|
|
'phone_country_code' => 45,
|
|
'phone' => $phone,
|
|
'permissions' => ['VEHICLES_LIST'],
|
|
'note' => 'Created by superuser test',
|
|
], $session['headers']);
|
|
|
|
$response
|
|
->assertStatus(200)
|
|
->assertEnvelope()
|
|
->assertSuccess();
|
|
|
|
$payload = $response->data();
|
|
$createdSubuserId = isset($payload['subuser']['id']) ? (int)$payload['subuser']['id'] : null;
|
|
$createdGrantId = isset($payload['grant']['id']) ? (int)$payload['grant']['id'] : null;
|
|
$setupToken = isset($payload['invite']['setup_token']) ? (string)$payload['invite']['setup_token'] : null;
|
|
|
|
expect($payload['subuser']['customer_number'] ?? null)->toBe((int)$customer['customer_number']);
|
|
expect($payload['subuser']['name'] ?? null)->toBe('Invited Driver');
|
|
expect($payload['subuser']['grant_permissions'] ?? null)->toBe(['VEHICLES_LIST']);
|
|
expect($payload['grant']['note'] ?? null)->toBe('Created by superuser test');
|
|
expect($payload['invite']['setup_link'] ?? null)->toBeString();
|
|
} finally {
|
|
if ($setupToken !== null && $setupToken !== '') {
|
|
(new \objects\subusers_o())->invalidateSetupToken($setupToken);
|
|
}
|
|
if ($createdGrantId !== null) {
|
|
api_test_runtime()->db()->query('DELETE FROM `subuser_grants` WHERE `id` = ' . $createdGrantId);
|
|
}
|
|
if ($createdSubuserId !== null) {
|
|
api_test_runtime()->db()->query('DELETE FROM `tokens` WHERE `user_id` = ' . $createdSubuserId . " AND `type` = 'AUTH_TOKEN_SUBUSER'");
|
|
api_test_runtime()->db()->query('DELETE FROM `subusers` WHERE `id` = ' . $createdSubuserId);
|
|
}
|
|
}
|
|
});
|