ImsLtiPlugin::getRoleScopeMentor()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 2
dl 0
loc 5
rs 10
c 0
b 0
f 0
1
<?php
2
3
/* For license terms, see /license.txt */
4
5
use Chamilo\CoreBundle\Entity\Course;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, Course. Consider defining an alias.

Let?s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let?s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
6
use Chamilo\CoreBundle\Entity\Session;
7
use Chamilo\CourseBundle\Entity\CTool;
8
use Chamilo\PluginBundle\Entity\ImsLti\ImsLtiTool;
9
use Chamilo\PluginBundle\Entity\ImsLti\LineItem;
10
use Chamilo\PluginBundle\Entity\ImsLti\Platform;
11
use Chamilo\PluginBundle\Entity\ImsLti\Token;
12
use Chamilo\UserBundle\Entity\User;
13
use Doctrine\ORM\Tools\SchemaTool;
14
use Firebase\JWT\JWK;
15
16
/**
17
 * Description of MsiLti.
18
 *
19
 * @author Angel Fernando Quiroz Campos <[email protected]>
20
 */
21
class ImsLtiPlugin extends Plugin
22
{
23
    const TABLE_TOOL = 'plugin_ims_lti_tool';
24
    const TABLE_PLATFORM = 'plugin_ims_lti_platform';
25
26
    public $isAdminPlugin = true;
27
28
    protected function __construct()
29
    {
30
        $version = '1.9.0';
31
        $author = 'Angel Fernando Quiroz Campos';
32
33
        $message = Display::return_message($this->get_lang('GenerateKeyPairInfo'));
34
        $settings = [
35
            $message => 'html',
36
            'enabled' => 'boolean',
37
        ];
38
39
        parent::__construct($version, $author, $settings);
40
41
        $this->setCourseSettings();
42
    }
43
44
    /**
45
     * Get the class instance.
46
     *
47
     * @staticvar MsiLtiPlugin $result
48
     *
49
     * @return ImsLtiPlugin
50
     */
51
    public static function create()
52
    {
53
        static $result = null;
54
55
        return $result ?: $result = new self();
56
    }
57
58
    /**
59
     * Get the plugin directory name.
60
     */
61
    public function get_name()
62
    {
63
        return 'ims_lti';
64
    }
65
66
    /**
67
     * Install the plugin. Setup the database.
68
     *
69
     * @throws \Doctrine\ORM\Tools\ToolsException
70
     */
71
    public function install()
72
    {
73
        $this->createPluginTables();
74
    }
75
76
    /**
77
     * Save configuration for plugin.
78
     *
79
     * Generate a new key pair for platform when enabling plugin.
80
     *
81
     * @throws \Doctrine\ORM\OptimisticLockException
82
     *
83
     * @return $this|Plugin
84
     */
85
    public function performActionsAfterConfigure()
86
    {
87
        $em = Database::getManager();
88
89
        /** @var Platform $platform */
90
        $platform = $em
91
            ->getRepository('ChamiloPluginBundle:ImsLti\Platform')
92
            ->findOneBy([]);
93
94
        if ($this->get('enabled') === 'true') {
95
            if (!$platform) {
0 ignored issues
show
introduced by
$platform is of type Chamilo\PluginBundle\Entity\ImsLti\Platform, thus it always evaluated to true.
Loading history...
96
                $platform = new Platform();
97
            }
98
99
            $keyPair = self::generatePlatformKeys();
100
101
            $platform->setKid($keyPair['kid']);
102
            $platform->publicKey = $keyPair['public'];
103
            $platform->setPrivateKey($keyPair['private']);
104
105
            $em->persist($platform);
106
        } else {
107
            if ($platform) {
0 ignored issues
show
introduced by
$platform is of type Chamilo\PluginBundle\Entity\ImsLti\Platform, thus it always evaluated to true.
Loading history...
108
                $em->remove($platform);
109
            }
110
        }
111
112
        $em->flush();
113
114
        return $this;
115
    }
116
117
    /**
118
     * Unistall plugin. Clear the database.
119
     */
120
    public function uninstall()
121
    {
122
        try {
123
            $this->dropPluginTables();
124
            $this->removeTools();
125
        } catch (Exception $e) {
126
            error_log('Error while uninstalling IMS/LTI plugin: '.$e->getMessage());
127
        }
128
    }
129
130
    /**
131
     * @return CTool
132
     */
133
    public function findCourseToolByLink(Course $course, ImsLtiTool $ltiTool)
134
    {
135
        $em = Database::getManager();
136
        $toolRepo = $em->getRepository('ChamiloCourseBundle:CTool');
137
138
        /** @var CTool $cTool */
139
        $cTool = $toolRepo->findOneBy(
140
            [
141
                'cId' => $course,
142
                'link' => self::generateToolLink($ltiTool),
143
            ]
144
        );
145
146
        return $cTool;
147
    }
148
149
    /**
150
     * @throws \Doctrine\ORM\OptimisticLockException
151
     */
152
    public function updateCourseTool(CTool $courseTool, ImsLtiTool $ltiTool)
153
    {
154
        $em = Database::getManager();
155
156
        $courseTool->setName($ltiTool->getName());
157
158
        if ('iframe' !== $ltiTool->getDocumentTarget()) {
159
            $courseTool->setTarget('_blank');
160
        } else {
161
            $courseTool->setTarget('_self');
162
        }
163
164
        $em->persist($courseTool);
165
        $em->flush();
166
    }
167
168
    /**
169
     * Add the course tool.
170
     *
171
     * @param bool $isVisible
172
     *
173
     * @throws \Doctrine\ORM\OptimisticLockException
174
     */
175
    public function addCourseTool(Course $course, ImsLtiTool $ltiTool, $isVisible = true)
176
    {
177
        $cTool = $this->createLinkToCourseTool(
178
            $ltiTool->getName(),
179
            $course->getId(),
180
            null,
181
            self::generateToolLink($ltiTool)
182
        );
183
        $cTool
184
            ->setTarget(
185
                $ltiTool->getDocumentTarget() === 'iframe' ? '_self' : '_blank'
186
            )
187
            ->setVisibility($isVisible);
188
189
        $em = Database::getManager();
190
        $em->persist($cTool);
191
        $em->flush();
192
    }
193
194
    /**
195
     * Add the course session tool.
196
     *
197
     * @param bool $isVisible
198
     *
199
     * @throws \Doctrine\ORM\OptimisticLockException
200
     */
201
    public function addCourseSessionTool(Course $course, Session $session, ImsLtiTool $ltiTool, $isVisible = true)
202
    {
203
        $cTool = $this->createLinkToCourseTool(
204
            $ltiTool->getName(),
205
            $course->getId(),
206
            null,
207
            self::generateToolLink($ltiTool),
208
            $session->getId()
209
        );
210
        $cTool
211
            ->setTarget(
212
                $ltiTool->getDocumentTarget() === 'iframe' ? '_self' : '_blank'
213
            )
214
            ->setVisibility($isVisible);
215
216
        $em = Database::getManager();
217
        $em->persist($cTool);
218
        $em->flush();
219
    }
220
221
    public static function isInstructor()
222
    {
223
        api_is_allowed_to_edit(false, true);
224
    }
225
226
    /**
227
     * @return array
228
     */
229
    public static function getRoles(User $user)
230
    {
231
        $roles = ['http://purl.imsglobal.org/vocab/lis/v2/system/person#User'];
232
233
        if (DRH === $user->getStatus()) {
234
            $roles[] = 'http://purl.imsglobal.org/vocab/lis/v2/membership#Mentor';
235
            $roles[] = 'http://purl.imsglobal.org/vocab/lis/v2/institution/person#Mentor';
236
237
            return $roles;
238
        }
239
240
        if (!api_is_allowed_to_edit(false, true)) {
241
            $roles[] = 'http://purl.imsglobal.org/vocab/lis/v2/membership#Learner';
242
            $roles[] = 'http://purl.imsglobal.org/vocab/lis/v2/institution/person#Student';
243
244
            if ($user->getStatus() === INVITEE) {
245
                $roles[] = 'http://purl.imsglobal.org/vocab/lis/v2/institution/person#Guest';
246
            }
247
248
            return $roles;
249
        }
250
251
        $roles[] = 'http://purl.imsglobal.org/vocab/lis/v2/institution/person#Instructor';
252
        $roles[] = 'http://purl.imsglobal.org/vocab/lis/v2/membership#Instructor';
253
254
        if (api_is_platform_admin_by_id($user->getId())) {
255
            $roles[] = 'http://purl.imsglobal.org/vocab/lis/v2/institution/person#Administrator';
256
            $roles[] = 'http://purl.imsglobal.org/vocab/lis/v2/system/person#SysAdmin';
257
            $roles[] = 'http://purl.imsglobal.org/vocab/lis/v2/system/person#Administrator';
258
        }
259
260
        return $roles;
261
    }
262
263
    /**
264
     * @return string
265
     */
266
    public static function getUserRoles(User $user)
267
    {
268
        if (DRH === $user->getStatus()) {
269
            return 'urn:lti:role:ims/lis/Mentor';
270
        }
271
272
        if ($user->getStatus() === INVITEE) {
273
            return 'Learner,urn:lti:role:ims/lis/Learner/GuestLearner';
274
        }
275
276
        if (!api_is_allowed_to_edit(false, true)) {
277
            return 'Learner';
278
        }
279
280
        $roles = ['Instructor'];
281
282
        if (api_is_platform_admin_by_id($user->getId())) {
283
            $roles[] = 'urn:lti:role:ims/lis/Administrator';
284
        }
285
286
        return implode(',', $roles);
287
    }
288
289
    /**
290
     * @param int $userId
291
     *
292
     * @return string
293
     */
294
    public static function generateToolUserId($userId)
295
    {
296
        $siteName = api_get_setting('siteName');
297
        $institution = api_get_setting('Institution');
298
        $toolUserId = "$siteName - $institution - $userId";
299
        $toolUserId = api_replace_dangerous_char($toolUserId);
300
301
        return $toolUserId;
302
    }
303
304
    /**
305
     * @return string
306
     */
307
    public static function getLaunchUserIdClaim(ImsLtiTool $tool, User $user)
308
    {
309
        if (null !== $tool->getParent()) {
310
            $tool = $tool->getParent();
311
        }
312
313
        $replacement = $tool->getReplacementForUserId();
314
315
        if (empty($replacement)) {
316
            if ($tool->getVersion() === ImsLti::V_1P1) {
317
                return self::generateToolUserId($user->getId());
318
            }
319
320
            return (string) $user->getId();
321
        }
322
323
        $replaced = str_replace(
324
            ['$User.id', '$User.username'],
325
            [$user->getId(), $user->getUsername()],
326
            $replacement
327
        );
328
329
        return $replaced;
330
    }
331
332
    /**
333
     * @return string
334
     */
335
    public static function getRoleScopeMentor(User $currentUser, ImsLtiTool $tool)
336
    {
337
        $scope = self::getRoleScopeMentorAsArray($currentUser, $tool, true);
338
339
        return implode(',', $scope);
340
    }
341
342
    /**
343
     * Tool User IDs which the user DRH can access as a mentor.
344
     *
345
     * @param bool $generateIdForTool. Optional. Set TRUE for LTI 1.x.
346
     *
347
     * @return array
348
     */
349
    public static function getRoleScopeMentorAsArray(User $user, ImsLtiTool $tool, $generateIdForTool = false)
350
    {
351
        if (DRH !== $user->getStatus()) {
352
            return [];
353
        }
354
355
        $followedUsers = UserManager::get_users_followed_by_drh($user->getId(), 0, true);
356
        $scope = [];
357
        /** @var array $userInfo */
358
        foreach ($followedUsers as $userInfo) {
359
            if ($generateIdForTool) {
360
                $followedUser = api_get_user_entity($userInfo['user_id']);
361
362
                $scope[] = self::getLaunchUserIdClaim($tool, $followedUser);
363
            } else {
364
                $scope[] = (string) $userInfo['user_id'];
365
            }
366
        }
367
368
        return $scope;
369
    }
370
371
    /**
372
     * @throws \Doctrine\ORM\OptimisticLockException
373
     */
374
    public function saveItemAsLtiLink(array $contentItem, ImsLtiTool $baseLtiTool, Course $course)
375
    {
376
        $em = Database::getManager();
377
        $ltiToolRepo = $em->getRepository('ChamiloPluginBundle:ImsLti\ImsLtiTool');
378
379
        $url = empty($contentItem['url']) ? $baseLtiTool->getLaunchUrl() : $contentItem['url'];
380
381
        /** @var ImsLtiTool $newLtiTool */
382
        $newLtiTool = $ltiToolRepo->findOneBy(['launchUrl' => $url, 'parent' => $baseLtiTool, 'course' => $course]);
383
384
        if (null === $newLtiTool) {
385
            $newLtiTool = new ImsLtiTool();
386
            $newLtiTool
387
                ->setLaunchUrl($url)
388
                ->setParent(
389
                    $baseLtiTool
390
                )
391
                ->setPrivacy(
392
                    $baseLtiTool->isSharingName(),
393
                    $baseLtiTool->isSharingEmail(),
394
                    $baseLtiTool->isSharingPicture()
395
                )
396
                ->setCourse($course);
397
        }
398
399
        $newLtiTool
400
            ->setName(
401
                !empty($contentItem['title']) ? $contentItem['title'] : $baseLtiTool->getName()
402
            )
403
            ->setDescription(
404
                !empty($contentItem['text']) ? $contentItem['text'] : null
405
            );
406
407
        if (!empty($contentItem['custom'])) {
408
            $newLtiTool
409
                ->setCustomParams(
410
                    $newLtiTool->encodeCustomParams($contentItem['custom'])
411
                );
412
        }
413
414
        $em->persist($newLtiTool);
415
        $em->flush();
416
417
        $courseTool = $this->findCourseToolByLink($course, $newLtiTool);
418
419
        if ($courseTool) {
0 ignored issues
show
introduced by
$courseTool is of type Chamilo\CourseBundle\Entity\CTool, thus it always evaluated to true.
Loading history...
420
            $this->updateCourseTool($courseTool, $newLtiTool);
421
422
            return;
423
        }
424
425
        $this->addCourseTool($course, $newLtiTool);
426
    }
427
428
    /**
429
     * @return ImsLtiServiceResponse|null
430
     */
431
    public function processServiceRequest()
432
    {
433
        $xml = $this->getRequestXmlElement();
434
435
        if (empty($xml)) {
436
            return null;
437
        }
438
439
        $request = ImsLtiServiceRequestFactory::create($xml);
440
441
        return $request->process();
442
    }
443
444
    /**
445
     * @param int $toolId
446
     *
447
     * @return bool
448
     */
449
    public static function existsToolInCourse($toolId, Course $course)
450
    {
451
        $em = Database::getManager();
452
        $toolRepo = $em->getRepository('ChamiloPluginBundle:ImsLti\ImsLtiTool');
453
454
        /** @var ImsLtiTool $tool */
455
        $tool = $toolRepo->findOneBy(['id' => $toolId, 'course' => $course]);
456
457
        return !empty($tool);
458
    }
459
460
    /**
461
     * @param string $configUrl
462
     *
463
     * @throws Exception
464
     *
465
     * @return string
466
     */
467
    public function getLaunchUrlFromCartridge($configUrl)
468
    {
469
        $options = [
470
            CURLOPT_CUSTOMREQUEST => 'GET',
471
            CURLOPT_POST => false,
472
            CURLOPT_RETURNTRANSFER => true,
473
            CURLOPT_HEADER => false,
474
            CURLOPT_FOLLOWLOCATION => true,
475
            CURLOPT_ENCODING => '',
476
            CURLOPT_SSL_VERIFYPEER => false,
477
        ];
478
479
        $ch = curl_init($configUrl);
480
        curl_setopt_array($ch, $options);
481
        $content = curl_exec($ch);
482
        $errno = curl_errno($ch);
483
        curl_close($ch);
484
485
        if ($errno !== 0) {
486
            throw new Exception($this->get_lang('NoAccessToUrl'));
487
        }
488
489
        $xml = new SimpleXMLElement($content);
490
        $result = $xml->xpath('blti:launch_url');
491
492
        if (empty($result)) {
493
            throw new Exception($this->get_lang('LaunchUrlNotFound'));
494
        }
495
496
        $launchUrl = $result[0];
497
498
        return (string) $launchUrl;
499
    }
500
501
    public function trimParams(array &$params)
502
    {
503
        foreach ($params as $key => $value) {
504
            $newValue = preg_replace('/\s+/', ' ', $value);
505
            $params[$key] = trim($newValue);
506
        }
507
    }
508
509
    /**
510
     * @return array
511
     */
512
    public function removeUrlParamsFromLaunchParams(ImsLtiTool $tool, array &$params)
513
    {
514
        $urlQuery = parse_url($tool->getLaunchUrl(), PHP_URL_QUERY);
515
516
        if (empty($urlQuery)) {
517
            return $params;
518
        }
519
520
        $queryParams = [];
521
        parse_str($urlQuery, $queryParams);
522
        $queryKeys = array_keys($queryParams);
523
524
        foreach ($queryKeys as $key) {
525
            if (isset($params[$key])) {
526
                unset($params[$key]);
527
            }
528
        }
529
    }
530
531
    /**
532
     * Avoid conflict with foreign key when deleting a course.
533
     *
534
     * @param int $courseId
535
     */
536
    public function doWhenDeletingCourse($courseId)
537
    {
538
        $em = Database::getManager();
539
        $q = $em
540
            ->createQuery(
541
                'DELETE FROM ChamiloPluginBundle:ImsLti\ImsLtiTool tool
542
                    WHERE tool.course = :c_id and tool.parent IS NOT NULL'
543
            );
544
        $q->execute(['c_id' => (int) $courseId]);
545
546
        $em->createQuery('DELETE FROM ChamiloPluginBundle:ImsLti\ImsLtiTool tool WHERE tool.course = :c_id')
547
            ->execute(['c_id' => (int) $courseId]);
548
    }
549
550
    /**
551
     * @return string
552
     */
553
    public static function getIssuerUrl()
554
    {
555
        $webPath = api_get_path(WEB_PATH);
556
557
        return trim($webPath, " /");
558
    }
559
560
    public static function getCoursesForParentTool(ImsLtiTool $tool, Session $session = null)
561
    {
562
        if ($tool->getParent()) {
563
            return [];
564
        }
565
566
        $children = $tool->getChildren();
567
568
        if ($session) {
569
            $children = $children->filter(function (ImsLtiTool $tool) use ($session) {
570
                if (null === $tool->getSession()) {
571
                    return false;
572
                }
573
574
                if ($tool->getSession()->getId() !== $session->getId()) {
575
                    return false;
576
                }
577
578
                return true;
579
            });
580
        }
581
582
        return $children->map(function (ImsLtiTool $tool) {
583
            return $tool->getCourse();
584
        });
585
    }
586
587
    /**
588
     * It gets the public key from jwks or rsa keys.
589
     *
590
     * @param ImsLtiTool $tool
591
     *
592
     * @return mixed|string|null
593
     */
594
    public static function getToolPublicKey(ImsLtiTool $tool)
595
    {
596
        $publicKey = '';
597
        if (!empty($tool->getJwksUrl())) {
598
            $publicKeySet = json_decode(file_get_contents($tool->getJwksUrl()), true);
599
            $pk = [];
600
            foreach ($publicKeySet['keys'] as $key) {
601
                $pk = openssl_pkey_get_details(
602
                    JWK::parseKeySet(['keys' => [$key]])[$key['kid']]
603
                );
604
            }
605
            if (!empty($pk)) {
606
                $publicKey = $pk['key'];
607
            }
608
        } else {
609
            $publicKey = $tool->publicKey;
610
        };
611
612
        return $publicKey;
613
    }
614
615
    /**
616
     * @return string
617
     */
618
    protected function getConfigExtraText()
619
    {
620
        $text = $this->get_lang('ImsLtiDescription');
621
        $text .= sprintf(
622
            $this->get_lang('ManageToolButton'),
623
            api_get_path(WEB_PLUGIN_PATH).'ims_lti/admin.php'
624
        );
625
626
        return $text;
627
    }
628
629
    /**
630
     * Creates the plugin tables on database.
631
     *
632
     * @throws \Doctrine\ORM\Tools\ToolsException
633
     */
634
    private function createPluginTables()
635
    {
636
        $em = Database::getManager();
637
638
        if ($em->getConnection()->getSchemaManager()->tablesExist([self::TABLE_TOOL])) {
639
            return;
640
        };
641
642
        $schemaTool = new SchemaTool($em);
643
        $schemaTool->createSchema(
644
            [
645
                $em->getClassMetadata(ImsLtiTool::class),
646
                $em->getClassMetadata(LineItem::class),
647
                $em->getClassMetadata(Platform::class),
648
                $em->getClassMetadata(Token::class),
649
            ]
650
        );
651
    }
652
653
    /**
654
     * Drops the plugin tables on database.
655
     */
656
    private function dropPluginTables()
657
    {
658
        $em = Database::getManager();
659
660
        if (!$em->getConnection()->getSchemaManager()->tablesExist([self::TABLE_TOOL])) {
661
            return;
662
        };
663
664
        $schemaTool = new SchemaTool($em);
665
        $schemaTool->dropSchema(
666
            [
667
                $em->getClassMetadata(ImsLtiTool::class),
668
                $em->getClassMetadata(LineItem::class),
669
                $em->getClassMetadata(Platform::class),
670
                $em->getClassMetadata(Token::class),
671
            ]
672
        );
673
    }
674
675
    private function removeTools()
676
    {
677
        $sql = "DELETE FROM c_tool WHERE link LIKE 'ims_lti/start.php%' AND category = 'plugin'";
678
        Database::query($sql);
679
    }
680
681
    /**
682
     * Set the course settings.
683
     */
684
    private function setCourseSettings()
685
    {
686
        $button = Display::toolbarButton(
687
            $this->get_lang('ConfigureExternalTool'),
688
            api_get_path(WEB_PLUGIN_PATH).'ims_lti/configure.php?'.api_get_cidreq(),
689
            'cog',
690
            'primary'
691
        );
692
693
        // This setting won't be saved in the database.
694
        $this->course_settings = [
695
            [
696
                'name' => $this->get_lang('ImsLtiDescription').$button.'<hr>',
697
                'type' => 'html',
698
            ],
699
        ];
700
    }
701
702
    /**
703
     * @return string
704
     */
705
    private static function generateToolLink(ImsLtiTool $tool)
706
    {
707
        return 'ims_lti/start.php?id='.$tool->getId();
708
    }
709
710
    /**
711
     * @return SimpleXMLElement|null
712
     */
713
    private function getRequestXmlElement()
714
    {
715
        $request = file_get_contents("php://input");
716
717
        if (empty($request)) {
718
            return null;
719
        }
720
721
        return new SimpleXMLElement($request);
722
    }
723
724
    /**
725
     * Generate a key pair and key id for the platform.
726
     *
727
     * Rerturn a associative array like ['kid' => '...', 'private' => '...', 'public' => '...'].
728
     *
729
     * @return array
730
     */
731
    private static function generatePlatformKeys()
732
    {
733
        // Create the private and public key
734
        $res = openssl_pkey_new(
735
            [
736
                'digest_alg' => 'sha256',
737
                'private_key_bits' => 2048,
738
                'private_key_type' => OPENSSL_KEYTYPE_RSA,
739
            ]
740
        );
741
742
        // Extract the private key from $res to $privateKey
743
        $privateKey = '';
744
        openssl_pkey_export($res, $privateKey);
745
746
        // Extract the public key from $res to $publicKey
747
        $publicKey = openssl_pkey_get_details($res);
748
749
        return [
750
            'kid' => bin2hex(openssl_random_pseudo_bytes(10)),
751
            'private' => $privateKey,
752
            'public' => $publicKey["key"],
753
        ];
754
    }
755
}
756