Passed
Push — 1.11.x ( 080d0e...e78a37 )
by Angel Fernando Quiroz
10:16
created

LtiProviderPlugin::createPluginTables()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 29
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 3
eloc 23
c 2
b 0
f 0
nc 3
nop 0
dl 0
loc 29
rs 9.552
1
<?php
2
/* For license terms, see /license.txt */
3
4
use Chamilo\PluginBundle\Entity\LtiProvider\PlatformKey;
5
use Doctrine\DBAL\DBALException;
6
use Doctrine\ORM\OptimisticLockException;
7
use Symfony\Component\Filesystem\Filesystem;
8
9
/**
10
 * Description of LtiProvider.
11
 *
12
 * @author Christian Beeznest <[email protected]>
13
 */
14
class LtiProviderPlugin extends Plugin
15
{
16
    const TABLE_PLATFORM = 'plugin_lti_provider_platform';
17
18
    public $isAdminPlugin = true;
19
20
    protected function __construct()
21
    {
22
        $version = '1.0';
23
        $author = 'Christian Beeznest';
24
25
        $message = Display::return_message($this->get_lang('Description'));
26
27
        if ($this->areTablesCreated()) {
28
            $publicKey = $this->getPublicKey();
29
30
            $pkHtml = '<div class="form-group">
31
                    <label for="lti_provider_public_key" class="col-sm-2 control-label">'
32
                .$this->get_lang('PublicKey').'</label>
33
                    <div class="col-sm-8">
34
                        <pre>'.$publicKey.'</pre>
35
                    </div>
36
                    <div class="col-sm-2"></div>
37
                </div>';
38
        } else {
39
            $pkHtml = $this->get_lang('GenerateKeyPairInfo');
40
        }
41
42
        $settings = [
43
            $message => 'html',
44
            'name' => 'text',
45
            'launch_url' => 'text',
46
            'login_url' => 'text',
47
            'redirect_url' => 'text',
48
            $pkHtml => 'html',
49
            'enabled' => 'boolean',
50
        ];
51
        parent::__construct($version, $author, $settings);
52
    }
53
54
    /**
55
     * Get the public key.
56
     */
57
    public function getPublicKey(): string
58
    {
59
        $publicKey = '';
60
        $platformKey = Database::getManager()
61
           ->getRepository('ChamiloPluginBundle:LtiProvider\PlatformKey')
62
           ->findOneBy([]);
63
64
        if ($platformKey) {
65
            $publicKey = $platformKey->getPublicKey();
66
        }
67
68
        return $publicKey;
69
    }
70
71
    /**
72
     * Get the class instance.
73
     *
74
     * @staticvar LtiProviderPlugin $result
75
     */
76
    public static function create(): LtiProviderPlugin
77
    {
78
        static $result = null;
79
80
        return $result ?: $result = new self();
81
    }
82
83
    /**
84
     * Check whether the current user is a teacher in this context.
85
     */
86
    public static function isInstructor()
87
    {
88
        api_is_allowed_to_edit(false, true);
89
    }
90
91
    /**
92
     * Get the plugin directory name.
93
     */
94
    public function get_name(): string
95
    {
96
        return 'lti_provider';
97
    }
98
99
    /**
100
     * Install the plugin. Set the database up.
101
     */
102
    public function install()
103
    {
104
        $pluginEntityPath = $this->getEntityPath();
105
106
        if (!is_dir($pluginEntityPath)) {
107
            if (!is_writable(dirname($pluginEntityPath))) {
108
                $message = get_lang('ErrorCreatingDir').': '.$pluginEntityPath;
109
                Display::addFlash(Display::return_message($message, 'error'));
110
111
                return;
112
            }
113
114
            mkdir($pluginEntityPath, api_get_permissions_for_new_directories());
115
        }
116
117
        $fs = new Filesystem();
118
        $fs->mirror(__DIR__.'/Entity/', $pluginEntityPath, null, ['override']);
119
120
        $this->createPluginTables();
121
    }
122
123
    /**
124
     * Get current entity sys path.
125
     */
126
    public function getEntityPath(): string
127
    {
128
        return api_get_path(SYS_PATH).'src/Chamilo/PluginBundle/Entity/'.$this->getCamelCaseName();
129
    }
130
131
    /**
132
     * Save configuration for plugin.
133
     *
134
     * Generate a new key pair for platform when enabling plugin.
135
     *
136
     * @throws OptimisticLockException
137
     * @throws \Doctrine\ORM\ORMException
138
     *
139
     * @return $this|Plugin
140
     */
141
    public function performActionsAfterConfigure()
142
    {
143
        $em = Database::getManager();
144
145
        /** @var PlatformKey $platformKey */
146
        $platformKey = $em
147
            ->getRepository('ChamiloPluginBundle:LtiProvider\PlatformKey')
148
            ->findOneBy([]);
149
150
        if ($this->get('enabled') === 'true') {
151
            if (!$platformKey) {
0 ignored issues
show
introduced by
$platformKey is of type Chamilo\PluginBundle\Ent...LtiProvider\PlatformKey, thus it always evaluated to true.
Loading history...
152
                $platformKey = new PlatformKey();
153
            }
154
155
            $keyPair = self::generatePlatformKeys();
156
157
            $platformKey->setKid($keyPair['kid']);
158
            $platformKey->publicKey = $keyPair['public'];
159
            $platformKey->setPrivateKey($keyPair['private']);
160
161
            $em->persist($platformKey);
162
        } else {
163
            if ($platformKey) {
0 ignored issues
show
introduced by
$platformKey is of type Chamilo\PluginBundle\Ent...LtiProvider\PlatformKey, thus it always evaluated to true.
Loading history...
164
                $em->remove($platformKey);
165
            }
166
        }
167
168
        $em->flush();
169
170
        return $this;
171
    }
172
173
    /**
174
     * Unistall plugin. Clear the database.
175
     */
176
    public function uninstall()
177
    {
178
        $pluginEntityPath = $this->getEntityPath();
179
        $fs = new Filesystem();
180
181
        if ($fs->exists($pluginEntityPath)) {
182
            $fs->remove($pluginEntityPath);
183
        }
184
185
        try {
186
            $this->dropPluginTables();
187
        } catch (DBALException $e) {
188
            error_log('Error while uninstalling IMS/LTI plugin: '.$e->getMessage());
189
        }
190
    }
191
192
    public function trimParams(array &$params)
193
    {
194
        foreach ($params as $key => $value) {
195
            $newValue = preg_replace('/\s+/', ' ', $value);
196
            $params[$key] = trim($newValue);
197
        }
198
    }
199
200
    /**
201
     * Creates the plugin tables on database.
202
     */
203
    private function createPluginTables(): void
204
    {
205
        if ($this->areTablesCreated()) {
206
            return;
207
        }
208
209
        $queries = [
210
            "CREATE TABLE plugin_lti_provider_platform (
211
                id int NOT NULL AUTO_INCREMENT,
212
                issuer varchar(255) NOT NULL,
213
                client_id varchar(255) NOT NULL,
214
                kid varchar(255) NOT NULL,
215
                auth_login_url varchar(255) NOT NULL,
216
                auth_token_url varchar(255) NOT NULL,
217
                key_set_url varchar(255) NOT NULL,
218
                deployment_id varchar(255) NOT NULL,
219
                PRIMARY KEY(id)
220
            ) DEFAULT CHARACTER SET utf8 COLLATE `utf8_unicode_ci` ENGINE = InnoDB",
221
            "CREATE TABLE plugin_lti_provider_platform_key (
222
                    id INT AUTO_INCREMENT NOT NULL,
223
                    kid VARCHAR(255) NOT NULL,
224
                    public_key LONGTEXT NOT NULL,
225
                    private_key LONGTEXT NOT NULL,
226
                    PRIMARY KEY(id)
227
                ) DEFAULT CHARACTER SET utf8 COLLATE `utf8_unicode_ci` ENGINE = InnoDB",
228
        ];
229
230
        foreach ($queries as $query) {
231
            Database::query($query);
232
        }
233
    }
234
235
    private function areTablesCreated(): bool
236
    {
237
        $entityManager = Database::getManager();
238
        $connection = $entityManager->getConnection();
239
240
        return $connection->getSchemaManager()->tablesExist(self::TABLE_PLATFORM);
241
    }
242
243
    /**
244
     * Generate a key pair and key id for the platform.
245
     *
246
     * Return a associative array like ['kid' => '...', 'private' => '...', 'public' => '...'].
247
     */
248
    private static function generatePlatformKeys(): array
249
    {
250
        // Create the private and public key
251
        $res = openssl_pkey_new(
252
            [
253
                'digest_alg' => 'sha256',
254
                'private_key_bits' => 2048,
255
                'private_key_type' => OPENSSL_KEYTYPE_RSA,
256
            ]
257
        );
258
259
        // Extract the private key from $res to $privateKey
260
        $privateKey = '';
261
        openssl_pkey_export($res, $privateKey);
262
263
        // Extract the public key from $res to $publicKey
264
        $publicKey = openssl_pkey_get_details($res);
265
266
        return [
267
            'kid' => bin2hex(openssl_random_pseudo_bytes(10)),
268
            'private' => $privateKey,
269
            'public' => $publicKey["key"],
270
        ];
271
    }
272
273
    /**
274
     * Drops the plugin tables on database.
275
     */
276
    private function dropPluginTables(): bool
277
    {
278
        Database::query("DROP TABLE IF EXISTS plugin_lti_provider_platform");
279
        Database::query("DROP TABLE IF EXISTS plugin_lti_provider_platform_key");
280
281
        return true;
282
    }
283
284
    /**
285
     * Get a SimpleXMLElement object with the request received on php://input.
286
     *
287
     * @throws Exception
288
     */
289
    private function getRequestXmlElement(): ?SimpleXMLElement
0 ignored issues
show
Unused Code introduced by
The method getRequestXmlElement() is not used, and could be removed.

This check looks for private methods that have been defined, but are not used inside the class.

Loading history...
290
    {
291
        $request = file_get_contents("php://input");
292
293
        if (empty($request)) {
294
            return null;
295
        }
296
297
        return new SimpleXMLElement($request);
298
    }
299
}
300