Passed
Pull Request — 1.11.x (#4505)
by Angel Fernando Quiroz
16:24 queued 06:00
created

requestAuthToken()   A

Complexity

Conditions 5
Paths 6

Size

Total Lines 32
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 18
c 1
b 0
f 0
dl 0
loc 32
rs 9.3554
cc 5
nc 6
nop 0
1
<?php
2
3
/* For licensing terms, see /license.txt */
4
5
use Chamilo\PluginBundle\ExternalNotificationConnect\Entity\AccessToken;
6
use Doctrine\ORM\Tools\SchemaTool;
7
use Doctrine\ORM\Tools\ToolsException;
8
use Firebase\JWT\JWT;
9
use GuzzleHttp\Client;
10
use GuzzleHttp\Exception\ClientException;
11
use GuzzleHttp\Exception\GuzzleException;
12
use GuzzleHttp\Exception\ServerException;
13
14
class ExternalNotificationConnectPlugin extends Plugin implements HookPluginInterface
15
{
16
    public const SETTING_AUTH_URL = 'auth_url';
17
    public const SETTING_AUTH_USERNAME = 'auth_username';
18
    public const SETTING_AUTH_PASSWORD = 'auth_password';
19
    public const SETTING_NOTIFICATION_URL = 'notification_url';
20
    public const SETTING_NOTIFY_PORTFOLIO = 'notify_portfolio';
21
    public const SETTING_NOTIFY_LEARNPATH = 'notify_learnpath';
22
23
    protected function __construct()
24
    {
25
        $author = [
26
            'Angel Fernando Quiroz Campos <[email protected]>',
27
        ];
28
29
        $settings = [
30
            'tool_enable' => 'boolean',
31
            self::SETTING_AUTH_URL => 'text',
32
            self::SETTING_AUTH_USERNAME => 'text',
33
            self::SETTING_AUTH_PASSWORD => 'text',
34
            self::SETTING_NOTIFICATION_URL => 'text',
35
            self::SETTING_NOTIFY_PORTFOLIO => 'boolean',
36
            self::SETTING_NOTIFY_LEARNPATH => 'boolean',
37
        ];
38
39
        parent::__construct(
40
            '1.0',
41
            implode('; ', $author),
42
            $settings
43
        );
44
    }
45
46
    public static function create(): ?ExternalNotificationConnectPlugin
47
    {
48
        static $result = null;
49
50
        return $result ?: $result = new self();
51
    }
52
53
    public function performActionsAfterConfigure(): ExternalNotificationConnectPlugin
54
    {
55
        $portfolioItemAddedEvent = HookPortfolioItemAdded::create();
56
        $portfolioItemEditedEvent = HookPortfolioItemEdited::create();
57
        $portfolioItemDeletedEvent = HookPortfolioItemDeleted::create();
58
59
        $portfolioItemAddedObserver = ExternalNotificationConnectPortfolioItemAddedHookObserver::create();
60
        $portfolioItemEditedObserver = ExternalNotificationConnectPortfolioItemEditedHookObserver::create();
61
        $portfolioItemDeletedObserver = ExternalNotificationConnectPortfolioItemDeletedHookObserver::create();
62
63
        if ('true' === $this->get(self::SETTING_NOTIFY_PORTFOLIO)) {
64
            $portfolioItemAddedEvent->attach($portfolioItemAddedObserver);
65
            $portfolioItemEditedEvent->attach($portfolioItemEditedObserver);
66
            $portfolioItemDeletedEvent->attach($portfolioItemDeletedObserver);
67
        } else {
68
            $portfolioItemAddedEvent->detach($portfolioItemAddedObserver);
69
            $portfolioItemEditedEvent->detach($portfolioItemEditedObserver);
70
            $portfolioItemDeletedEvent->detach($portfolioItemDeletedObserver);
71
        }
72
73
        $lpCreatedEvent = HookLearningPathCreated::create();
74
75
        $lpCreatedObserver = ExternalNotificationConnectLearningPathCreatedHookObserver::create();
76
77
        if ('true' === $this->get(self::SETTING_NOTIFY_LEARNPATH)) {
78
            $lpCreatedEvent->attach($lpCreatedObserver);
79
        } else {
80
            $lpCreatedEvent->detach($lpCreatedObserver);
81
        }
82
83
        return $this;
84
    }
85
86
    public function installHook()
87
    {
88
    }
89
90
    public function uninstallHook()
91
    {
92
        $portfolioItemAddedEvent = HookPortfolioItemAdded::create();
93
        $portfolioItemEditedEvent = HookPortfolioItemEdited::create();
94
        $portfolioItemDeletedEvent = HookPortfolioItemDeleted::create();
95
        $lpCreatedEvent = HookLearningPathCreated::create();
96
97
        $portfolioItemAddedObserver = ExternalNotificationConnectPortfolioItemAddedHookObserver::create();
98
        $portfolioItemEditedObserver = ExternalNotificationConnectPortfolioItemEditedHookObserver::create();
99
        $portfolioItemDeletedObserver = ExternalNotificationConnectPortfolioItemDeletedHookObserver::create();
100
        $lpCreatedObserver = ExternalNotificationConnectLearningPathCreatedHookObserver::create();
101
102
        $portfolioItemAddedEvent->detach($portfolioItemAddedObserver);
103
        $portfolioItemEditedEvent->detach($portfolioItemEditedObserver);
104
        $portfolioItemDeletedEvent->detach($portfolioItemDeletedObserver);
105
        $lpCreatedEvent->detach($lpCreatedObserver);
106
    }
107
108
    public function install()
109
    {
110
        $em = Database::getManager();
111
112
        $schemaManager = $em->getConnection()->getSchemaManager();
113
114
        $tableExists = $schemaManager->tablesExist(['plugin_ext_notif_conn_access_token']);
115
116
        if ($tableExists) {
117
            return;
118
        }
119
120
        $this->installDBTables();
121
        $this->installHook();
122
    }
123
124
    public function uninstall()
125
    {
126
        $this->uninstallHook();
127
        $this->uninstallDBTables();
128
    }
129
130
    /**
131
     * @throws \Doctrine\ORM\OptimisticLockException
132
     * @throws \Doctrine\ORM\ORMException
133
     * @throws Exception
134
     */
135
    public function getAccessToken()
136
    {
137
        $em = Database::getManager();
138
        $tokenRepository = $em->getRepository(AccessToken::class);
139
140
        $accessToken = $tokenRepository->findOneBy(['isValid' => true]);
141
142
        if (!$accessToken) {
143
            $newToken = $this->requestAuthToken();
144
145
            $accessToken = (new AccessToken())
146
                ->setToken($newToken)
147
                ->setIsValid(true);
148
149
            $em->persist($accessToken);
150
            $em->flush();
151
        } else {
152
            $tks = explode('.', $accessToken->getToken());
153
154
            $payload = json_decode(JWT::urlsafeB64Decode($tks[1]), true);
155
156
            if (time() >= $payload['exp']) {
157
                $accessToken->setIsValid(false);
158
159
                $newToken = $this->requestAuthToken();
160
161
                $accessToken = (new AccessToken())
162
                    ->setToken($newToken)
163
                    ->setIsValid(true);
164
165
                $em->persist($accessToken);
166
167
                $em->flush();
168
            }
169
        }
170
171
        return $accessToken->getToken();
172
    }
173
174
    private function installDBTables()
175
    {
176
        $em = Database::getManager();
177
178
        try {
179
            (new SchemaTool($em))
180
                ->createSchema([
181
                    $em->getClassMetadata(AccessToken::class),
182
                ]);
183
        } catch (ToolsException $e) {
184
            return;
185
        }
186
    }
187
188
    private function uninstallDBTables()
189
    {
190
        $em = Database::getManager();
191
192
        (new SchemaTool($em))
193
            ->dropSchema([
194
                $em->getClassMetadata(AccessToken::class),
195
            ]);
196
    }
197
198
    /**
199
     * @throws Exception
200
     */
201
    private function requestAuthToken(): string
202
    {
203
        $client = new Client();
204
205
        try {
206
            $response = $client->request(
207
                'POST',
208
                $this->get(ExternalNotificationConnectPlugin::SETTING_AUTH_URL),
209
                [
210
                    'json' => [
211
                        'email' => $this->get(ExternalNotificationConnectPlugin::SETTING_AUTH_USERNAME),
212
                        'password' => $this->get(ExternalNotificationConnectPlugin::SETTING_AUTH_PASSWORD),
213
                    ],
214
                ]
215
            );
216
        } catch (ClientException|ServerException $e) {
217
            if (!$e->hasResponse()) {
218
                throw new Exception($e->getMessage());
219
            }
220
221
            $response = $e->getResponse();
222
        } catch (GuzzleException $e) {
223
            throw new Exception($e->getMessage());
224
        }
225
226
        $json = json_decode((string) $response->getBody(), true);
227
228
        if (201 !== $json['status']) {
229
            throw new Exception($json['message']);
230
        }
231
232
        return $json['data']['data']['token'];
233
    }
234
}
235