Passed
Push — master ( 7a508d...4b0712 )
by Angel Fernando Quiroz
10:57
created

AzureSyncAbstractCommand::getUpdateActionByRole()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 22
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 16
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 22
rs 9.7333
1
<?php
2
3
/* For licensing terms, see /license.txt */
4
5
declare(strict_types=1);
6
7
namespace Chamilo\CoreBundle\Command;
8
9
use Chamilo\CoreBundle\Entity\AzureSyncState;
10
use Chamilo\CoreBundle\Entity\User;
11
use Chamilo\CoreBundle\Helpers\AuthenticationConfigHelper;
12
use Chamilo\CoreBundle\Helpers\AzureAuthenticatorHelper;
13
use Chamilo\CoreBundle\Repository\AzureSyncStateRepository;
14
use Chamilo\CoreBundle\Repository\Node\UserRepository;
15
use Doctrine\ORM\EntityManagerInterface;
16
use Exception;
17
use Generator;
18
use GuzzleHttp\Exception\GuzzleException;
19
use KnpU\OAuth2ClientBundle\Client\ClientRegistry;
20
use KnpU\OAuth2ClientBundle\Client\Provider\AzureClient;
21
use League\OAuth2\Client\Provider\Exception\IdentityProviderException;
22
use League\OAuth2\Client\Token\AccessTokenInterface;
23
use Symfony\Component\Console\Command\Command;
24
use TheNetworg\OAuth2\Client\Provider\Azure;
25
26
abstract class AzureSyncAbstractCommand extends Command
27
{
28
    protected AzureClient $client;
29
30
    protected Azure $provider;
31
32
    protected array $providerParams = [];
33
34
    public function __construct(
35
        protected readonly ClientRegistry $clientRegistry,
36
        readonly AuthenticationConfigHelper $configHelper,
37
        readonly protected AzureAuthenticatorHelper $azureHelper,
38
        readonly protected AzureSyncStateRepository $syncStateRepo,
39
        protected readonly EntityManagerInterface $entityManager,
40
        protected readonly UserRepository $userRepository,
41
    ) {
42
        parent::__construct();
43
44
        $this->client = $this->clientRegistry->getClient('azure');
45
        $this->provider = $this->client->getOAuth2Provider();
46
        $this->providerParams = $configHelper->getProviderConfig('azure');
47
    }
48
49
    /**
50
     * @throws GuzzleException
51
     * @throws IdentityProviderException
52
     */
53
    protected function generateOrRefreshToken(?AccessTokenInterface &$token): void
54
    {
55
        if (!$token || ($token->getExpires() && !$token->getRefreshToken())) {
56
            $token = $this->provider->getAccessToken(
57
                'client_credentials',
58
                ['resource' => $this->provider->resource]
59
            );
60
        }
61
    }
62
63
    /**
64
     * @throws Exception
65
     */
66
    protected function getAzureUsers(): Generator
67
    {
68
        $userFields = [
69
            'givenName',
70
            'surname',
71
            'mail',
72
            'userPrincipalName',
73
            'businessPhones',
74
            'mobilePhone',
75
            'accountEnabled',
76
            'mailNickname',
77
            'id',
78
        ];
79
80
        if ($this->providerParams['script_users_delta']) {
81
            $usersDeltaLink = $this->syncStateRepo->findOneBy(['title' => AzureSyncState::USERS_DATALINK]);
82
83
            $query = $usersDeltaLink
84
                ? $usersDeltaLink->getValue()
85
                : sprintf('$select=%s', implode(',', $userFields));
86
        } else {
87
            $query = sprintf(
88
                '$top=%d&$select=%s',
89
                AzureSyncState::API_PAGE_SIZE,
90
                implode(',', $userFields)
91
            );
92
        }
93
94
        $token = null;
95
96
        do {
97
            try {
98
                $this->generateOrRefreshToken($token);
99
100
                $azureUsersRequest = $this->provider->request(
101
                    'get',
102
                    $this->providerParams['script_users_delta'] ? "users/delta?$query" : "users?$query",
103
                    $token
104
                );
105
            } catch (GuzzleException|Exception $e) {
106
                throw new Exception('Exception when requesting users from Azure: '.$e->getMessage());
107
            }
108
109
            $azureUsersInfo = $azureUsersRequest['value'] ?? [];
110
111
            foreach ($azureUsersInfo as $azureUserInfo) {
112
                $azureUserInfo['mail'] = $azureUserInfo['mail'] ?? null;
113
                $azureUserInfo['surname'] = $azureUserInfo['surname'] ?? null;
114
                $azureUserInfo['givenName'] = $azureUserInfo['givenName'] ?? null;
115
116
                yield $azureUserInfo;
117
            }
118
119
            $hasNextLink = false;
120
121
            if (!empty($azureUsersRequest['@odata.nextLink'])) {
122
                $hasNextLink = true;
123
                $query = parse_url($azureUsersRequest['@odata.nextLink'], PHP_URL_QUERY);
124
            }
125
126
            if ($this->providerParams['script_users_delta'] && !empty($azureUsersRequest['@odata.deltaLink'])) {
127
                $this->syncStateRepo->save(
128
                    AzureSyncState::USERS_DATALINK,
129
                    parse_url($azureUsersRequest['@odata.deltaLink'], PHP_URL_QUERY),
130
                );
131
            }
132
        } while ($hasNextLink);
133
    }
134
135
    /**
136
     * @return array<string, string|false>
137
     */
138
    public function getGroupUidByRole(): array
139
    {
140
        $groupUidList = [
141
            'admin' => $this->providerParams['group_id_admin'],
142
            'sessionAdmin' => $this->providerParams['group_id_session_admin'],
143
            'teacher' => $this->providerParams['group_id_teacher'],
144
        ];
145
146
        return array_filter($groupUidList);
147
    }
148
149
    /**
150
     * @return array<string, callable>
151
     */
152
    public function getUpdateActionByRole(): array
153
    {
154
        return [
155
            'admin' => function (User $user) {
156
                $user
157
                    ->setStatus(COURSEMANAGER)
158
                    ->addUserAsAdmin()
159
                    ->setRoleFromStatus(COURSEMANAGER)
160
                ;
161
            },
162
            'sessionAdmin' => function (User $user) {
163
                $user
164
                    ->setStatus(SESSIONADMIN)
165
                    ->removeUserAsAdmin()
166
                    ->setRoleFromStatus(SESSIONADMIN)
167
                ;
168
            },
169
            'teacher' => function (User $user) {
170
                $user
171
                    ->setStatus(COURSEMANAGER)
172
                    ->removeUserAsAdmin()
173
                    ->setRoleFromStatus(COURSEMANAGER)
174
                ;
175
            },
176
        ];
177
    }
178
179
    /**
180
     * @throws Exception
181
     */
182
    protected function getAzureGroupMembers(string $groupUid): Generator
183
    {
184
        $userFields = [
185
            'mail',
186
            'mailNickname',
187
            'id',
188
        ];
189
190
        $query = sprintf(
191
            '$top=%d&$select=%s',
192
            AzureSyncState::API_PAGE_SIZE,
193
            implode(',', $userFields)
194
        );
195
196
        $token = null;
197
198
        do {
199
            try {
200
                $this->generateOrRefreshToken($token);
201
202
                $azureGroupMembersRequest = $this->provider->get(
203
                    "groups/$groupUid/members?$query",
204
                    $token
205
                );
206
            } catch (GuzzleException|Exception $e) {
207
                throw new Exception('Exception when requesting group members from Azure: '.$e->getMessage());
208
            }
209
210
            $azureGroupMembers = $azureGroupMembersRequest['value'] ?? [];
211
212
            foreach ($azureGroupMembers as $azureGroupMember) {
213
                yield $azureGroupMember;
214
            }
215
216
            $hasNextLink = false;
217
218
            if (!empty($azureGroupMembersRequest['@odata.nextLink'])) {
219
                $hasNextLink = true;
220
                $query = parse_url($azureGroupMembersRequest['@odata.nextLink'], PHP_URL_QUERY);
221
            }
222
        } while ($hasNextLink = false);
223
    }
224
}