Skip to content

Commit 3f1a3f3

Browse files
authored
Merge pull request #10045 from magento-cia/cia-2.4.9-alpha3-develop-bugfix-08252025
Cia 2.4.9 alpha3 develop Bugfix Delivery
2 parents 194d6e6 + de57619 commit 3f1a3f3

File tree

13 files changed

+1276
-14
lines changed

13 files changed

+1276
-14
lines changed
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<?php
2+
/**
3+
* Copyright 2025 Adobe
4+
* All Rights Reserved.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\LoginAsCustomer\Model\ResourceModel;
9+
10+
use Magento\Framework\App\ResourceConnection;
11+
use Magento\LoginAsCustomerApi\Api\DeleteAuthenticationDataForListOfUserInterface;
12+
13+
/**
14+
* @inheritdoc
15+
*/
16+
class DeleteAuthenticationDataForListOfUser implements DeleteAuthenticationDataForListOfUserInterface
17+
{
18+
/**
19+
* @var ResourceConnection
20+
*/
21+
private $resourceConnection;
22+
23+
/**
24+
* @param ResourceConnection $resourceConnection
25+
*/
26+
public function __construct(
27+
ResourceConnection $resourceConnection
28+
) {
29+
$this->resourceConnection = $resourceConnection;
30+
}
31+
32+
/**
33+
* @inheritdoc
34+
*/
35+
public function execute(array $userIds): void
36+
{
37+
$connection = $this->resourceConnection->getConnection();
38+
$tableName = $this->resourceConnection->getTableName('login_as_customer');
39+
40+
$connection->delete(
41+
$tableName,
42+
[
43+
'admin_id IN (?)' => $userIds
44+
]
45+
);
46+
}
47+
}
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
<?php
2+
/**
3+
* Copyright 2025 Adobe
4+
* All Rights Reserved.
5+
*/
6+
declare(strict_types=1);
7+
namespace Magento\LoginAsCustomer\Model\Validator;
8+
9+
use Magento\Authorization\Model\Rules;
10+
use Magento\Framework\Exception\LocalizedException;
11+
use Magento\User\Model\User as UserModel;
12+
use Magento\Authorization\Model\Acl\AclRetriever;
13+
use Magento\LoginAsCustomerApi\Api\ConfigInterface as LoginAsCustomerConfig;
14+
use Magento\Framework\Acl\RootResource;
15+
16+
/**
17+
* User role permission validator
18+
*/
19+
class UserRolePermission
20+
{
21+
/**
22+
* @var string
23+
*/
24+
private const ACL_RESOURCE = 'Magento_LoginAsCustomer::login';
25+
26+
/**
27+
* @param AclRetriever $aclRetriever
28+
* @param LoginAsCustomerConfig $config
29+
* @param RootResource $rootResource
30+
*/
31+
public function __construct(
32+
private readonly AclRetriever $aclRetriever,
33+
private readonly LoginAsCustomerConfig $config,
34+
private readonly RootResource $rootResource
35+
) {
36+
}
37+
38+
/**
39+
* Validate User role with specific resource
40+
*
41+
* @param Rules $rule
42+
* @return array []
43+
*/
44+
public function validateRoles(Rules $rule): array
45+
{
46+
$resources = $rule->getResources();
47+
$resources = is_array($resources) ? $resources : [];
48+
49+
if (!in_array(self::ACL_RESOURCE, $resources, true) &&
50+
!in_array($this->rootResource->getId(), $resources, true)
51+
) {
52+
$adminUsers = $rule->getRoleAssignedUsers();
53+
$adminUsers = is_array($adminUsers) ? $adminUsers : [];
54+
if (!empty($adminUsers)) {
55+
return $adminUsers;
56+
}
57+
}
58+
59+
return [];
60+
}
61+
62+
/**
63+
* Validate User with specific permission
64+
*
65+
* @param UserModel $result
66+
* @param int|null $oldRoleId
67+
* @return bool
68+
* @throws LocalizedException
69+
*/
70+
public function validateUser(UserModel $result, ?int $oldRoleId): bool
71+
{
72+
$newRoleId = (int) $result->getRoleId();
73+
return $oldRoleId !== $newRoleId
74+
&& $this->roleHasResource($newRoleId, self::ACL_RESOURCE);
75+
}
76+
77+
/**
78+
* Check if role has specific resource
79+
*
80+
* @param int $roleId
81+
* @param string $resourceId
82+
* @return bool
83+
* @throws LocalizedException
84+
*/
85+
private function roleHasResource(int $roleId, string $resourceId): bool
86+
{
87+
if (!$this->config->isEnabled()) {
88+
return false;
89+
}
90+
91+
$resources = $this->getRoleResources($roleId);
92+
$resourceSet = array_flip($resources);
93+
94+
return !isset($resourceSet[$resourceId]) && !isset($resourceSet[$this->rootResource->getId()]);
95+
}
96+
97+
/**
98+
* Get allowed resources for a role
99+
*
100+
* @param int $roleId
101+
* @return array
102+
* @throws LocalizedException
103+
*/
104+
private function getRoleResources(int $roleId): array
105+
{
106+
try {
107+
return $this->aclRetriever->getAllowedResourcesByRole($roleId);
108+
} catch (\Exception $e) {
109+
throw new LocalizedException(__('An error occurred while saving this user.'));
110+
}
111+
}
112+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
<?php
2+
/**
3+
* Copyright 2025 Adobe
4+
* All Rights Reserved.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\LoginAsCustomer\Plugin\Authorization\Model\ResourceModel;
9+
10+
use Exception;
11+
use Magento\Authorization\Model\Rules;
12+
use Magento\Authorization\Model\ResourceModel\Rules as Subject;
13+
use Magento\LoginAsCustomerApi\Api\DeleteAuthenticationDataForListOfUserInterface;
14+
use Magento\LoginAsCustomer\Model\Validator\UserRolePermission;
15+
use Psr\Log\LoggerInterface;
16+
17+
/**
18+
* Detect current users resources change
19+
*
20+
* @SuppressWarnings(PHPMD.CookieAndSessionMisuse)
21+
*/
22+
class RulesPlugin
23+
{
24+
/**
25+
* @param DeleteAuthenticationDataForListOfUserInterface $deleteAuthenticationDataForListOfUser
26+
* @param UserRolePermission $validator
27+
* @param LoggerInterface $logger
28+
*/
29+
public function __construct(
30+
private readonly DeleteAuthenticationDataForListOfUserInterface $deleteAuthenticationDataForListOfUser,
31+
private readonly UserRolePermission $validator,
32+
private readonly LoggerInterface $logger
33+
) {
34+
}
35+
36+
/**
37+
* Terminate 'Login as Customer' sessions when related permissions are revoked.
38+
*
39+
* @param Subject $subject
40+
* @param void $result
41+
* @param Rules $rule
42+
* @return void
43+
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
44+
*/
45+
public function afterSaveRel(Subject $subject, $result, Rules $rule): void
46+
{
47+
try {
48+
$usersToTerminate = array_merge(
49+
$this->validator->validateRoles($rule),
50+
$this->ensureArray($rule->getRoleUnassignedUsers())
51+
);
52+
if ($usersToTerminate) {
53+
$this->deleteAuthenticationDataForListOfUser->execute(array_unique($usersToTerminate));
54+
}
55+
} catch (Exception $e) {
56+
$this->logger->error($e->getMessage());
57+
}
58+
}
59+
60+
/**
61+
* Ensure a value is an array
62+
*
63+
* @param mixed $value
64+
* @return array
65+
*/
66+
private function ensureArray($value): array
67+
{
68+
return is_array($value) ? $value : [];
69+
}
70+
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
<?php
2+
/**
3+
* Copyright 2025 Adobe
4+
* All Rights Reserved.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\LoginAsCustomer\Plugin\User\Model;
9+
10+
use Exception;
11+
use Magento\Framework\Exception\LocalizedException;
12+
use Magento\LoginAsCustomerApi\Api\DeleteAuthenticationDataForUserInterface;
13+
use Magento\User\Api\Data\UserInterface;
14+
use Magento\User\Model\User as UserModel;
15+
use Magento\LoginAsCustomer\Model\Validator\UserRolePermission;
16+
use Psr\Log\LoggerInterface;
17+
18+
/**
19+
* Check whether role has been updated of existing user
20+
*
21+
* @SuppressWarnings(PHPMD.CookieAndSessionMisuse)
22+
*/
23+
class User
24+
{
25+
/**
26+
* @var array
27+
*/
28+
private array $originalRoleIds = [];
29+
30+
/**
31+
* @param DeleteAuthenticationDataForUserInterface $deleteAuthenticationDataForUser
32+
* @param UserRolePermission $validator
33+
* @param LoggerInterface $logger
34+
*/
35+
public function __construct(
36+
private readonly DeleteAuthenticationDataForUserInterface $deleteAuthenticationDataForUser,
37+
private readonly UserRolePermission $validator,
38+
private readonly LoggerInterface $logger
39+
) {
40+
}
41+
42+
/**
43+
* Capture original role ID before save
44+
*
45+
* @param UserModel $subject
46+
* @return void
47+
* @throws LocalizedException
48+
*/
49+
public function beforeSave(UserModel $subject)
50+
{
51+
if ($subject->getId()) {
52+
// Get current role IDs before save
53+
$currentRoles = $subject->getRoles();
54+
$this->originalRoleIds[(int) $subject->getId()] = $currentRoles ? (int) $currentRoles[0] : null;
55+
}
56+
}
57+
58+
/**
59+
* Terminate 'Login as Customer' sessions when related permissions are revoked.
60+
*
61+
* @param UserInterface $subject
62+
* @param UserModel $result
63+
* @return UserModel
64+
* @throws LocalizedException
65+
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
66+
*/
67+
public function afterSave(
68+
UserInterface $subject,
69+
UserModel $result
70+
) {
71+
try {
72+
if (!$result->getSkipRoleResourceValidation()) {
73+
$userId = (int) $result->getId();
74+
$oldRoleId = $this->originalRoleIds[$userId] ?? null;
75+
76+
if ($this->validator->validateUser($result, $oldRoleId)) {
77+
$this->deleteAuthenticationDataForUser->execute($userId);
78+
}
79+
}
80+
} catch (Exception $e) {
81+
$this->logger->error($e->getMessage());
82+
}
83+
84+
return $result;
85+
}
86+
}

0 commit comments

Comments
 (0)