Merge lp:~henrik-lochmann/goobi-presentation/bug-985487 into lp:goobi-presentation/1.1

Proposed by Henrik Lochmann
Status: Merged
Merged at revision: 108
Proposed branch: lp:~henrik-lochmann/goobi-presentation/bug-985487
Merge into: lp:goobi-presentation/1.1
Diff against target: 438 lines (+314/-7)
9 files modified
dlf/common/class.tx_dlf_solr.php (+30/-1)
dlf/ext_autoload.php (+1/-0)
dlf/ext_emconf.php (+1/-0)
dlf/ext_localconf.php (+2/-0)
dlf/plugins/search/class.tx_dlf_search.php (+108/-5)
dlf/plugins/search/class.tx_dlf_search_suggest.php (+140/-0)
dlf/plugins/search/search_suggest.js (+29/-0)
dlf/plugins/search/template.tmpl (+2/-1)
dlf/t3jquery.txt (+1/-0)
To merge this branch: bzr merge lp:~henrik-lochmann/goobi-presentation/bug-985487
Reviewer Review Type Date Requested Status
Henrik Lochmann (community) Needs Information
Sebastian Meyer Needs Fixing
Review via email: mp+104405@code.launchpad.net

Description of the change

Implementation of feature/wishlist bug #985487.

To post a comment you must log in.
Revision history for this message
Sebastian Meyer (sebastian-meyer) wrote :

Great work, but there are some more things to do:

- Please add t3jquery to the dependencies in ext_emconf.php.

- Check for the extension to be loaded und use its API to integrate the libraries.
  (see http://typo3.org/extension-manuals/t3jquery/2.2.0/view/1/5/)

- I suggest moving the getSolrSuggestUrl() method from tx_dlf_search_suggest to tx_dlf_solr. Also it should respect username and password if necessary (see solrConnect() in tx_dlf_solr) and handle the core correctly (currently the core isn't regarded at all although there is a variable named $core).

- According to the Coding Guidelines tx_dlf_search_suggest should be a child of tx_dlf_plugin instead of tslib_pibase.

- Please make all private methods protected in order to allow proper class inheritage.

Thanks! :o)

review: Needs Fixing
Revision history for this message
Henrik Lochmann (henrik-lochmann) wrote :

> Great work, but there are some more things to do:
>
> - Please add t3jquery to the dependencies in ext_emconf.php.
Ok.

> - Check for the extension to be loaded und use its API to integrate the
> libraries.
> (see http://typo3.org/extension-manuals/t3jquery/2.2.0/view/1/5/)
Ok.

> - I suggest moving the getSolrSuggestUrl() method from tx_dlf_search_suggest
> to tx_dlf_solr. Also it should respect username and password if necessary (see
> solrConnect() in tx_dlf_solr) and handle the core correctly (currently the
> core isn't regarded at all although there is a variable named $core).
Ok, see below.

> - According to the Coding Guidelines tx_dlf_search_suggest should be a child
> of tx_dlf_plugin instead of tslib_pibase.
The class tx_dlf_search_suggest works as eID-based Ajax backend for search suggestion requests. The eID mechanism of Typo3 allows to bypass Typo3 frontend rendering routines, which speeds communication massively up. Thus, the class tx_dlf_search_suggest is not part of and does not have access to frontend functionality, which is necessary for tx_dlf_plugin. Therefore, it currently does not inherit from tx_dlf_plugin as demanded by the coding guidelines.

Unfortunately, at least proper Solr core handling requires frontend access, because the core is adjusted in the corresponding frontend plugin instance of the rendered page. In order to avoid the instantiation of the frontend only to read the configured Solr core, I propose to render a hidden field, as part of the search form, that contains the atcual Solr core value.

> - Please make all private methods protected in order to allow proper class
> inheritage.
Ok.

review: Needs Information
Revision history for this message
Sebastian Meyer (sebastian-meyer) wrote :

Rendering a hidden field for the Solr core number in the search form could be a security issue, because the field's value can be manipulated and thus a user could query any Solr core.

Although I admit that initializing the whole TSFE object in tx_dlf_search_suggest creates a lot of overhead, I would nevertheless propose doing something like this:

http://www.zoe.vc/2008/typoscript-auslesen/

That way you could read the plugin's configuration and get the Solr core number without ignoring the proper access conditions.

review: Needs Fixing
Revision history for this message
Henrik Lochmann (henrik-lochmann) wrote :

I'm seriously concerned about the performance loss, coming with the instantiation of the t3 frontend for every single letter typed in the search field.

What if we encrypt the hidden field in a way that ensures its value is 1) unreadable for the user and 2) definitely unchanged when comes back to the server? For instance, Yavor Atanasov proposed a solution which seems pretty strong:

- http://yavkata.co.uk/weblog/php/securing-html-hidden-input-fields-using-encryption-and-hashing/

review: Needs Information
81. By Henrik Lochmann

- fixed review todos

Revision history for this message
Sebastian Meyer (sebastian-meyer) wrote :

This should be merged into goobi-presentation/1.1, so I deleted the merge proposal for goobi-presentation/1.2 and moved the proposal's text here:

Henrik Lochmann wrote:

Unfortunately, my local commit messages obviously did not reach LP. Thus, here at least a textual summary of the changes:

- dlf/ext_emconf.php: dependency to t3jquery
- dlf/ext_autoload.php: loads tx_dlf_search_suggest
- dlf/common/class.tx_dlf_solr.php: getSolrUrl() respects solr core

- dlf/plugins/search/*
+ t3jquery dependencies are properly included when using search suggestions
+ search form, suggestions and search template work on secured hidden input that contains the index name of the incorporated solr core (this code only works when PHP mcrypt extension is installed, otherwise nothing happens)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'dlf/common/class.tx_dlf_solr.php'
2--- dlf/common/class.tx_dlf_solr.php 2012-03-30 09:14:14 +0000
3+++ dlf/common/class.tx_dlf_solr.php 2012-06-04 15:53:22 +0000
4@@ -29,7 +29,7 @@
5 /**
6 * Solr class 'tx_dlf_solr' for the 'dlf' extension.
7 *
8- * @author Sebastian Meyer <sebastian.meyer@slub-dresden.de>
9+ * @author Sebastian Meyer <sebastian.meyer@slub-dresden.de>, Henrik Lochmann <dev@mentalmotive.com>
10 * @copyright Copyright (c) 2011, Sebastian Meyer, SLUB Dresden
11 * @package TYPO3
12 * @subpackage tx_dlf
13@@ -46,6 +46,35 @@
14 public static $extKey = 'dlf';
15
16 /**
17+ * Returns the request url for Solr-based search suggestions.
18+ *
19+ * @access public
20+ *
21+ * @return string The request url for Solr-based search suggestions.
22+ */
23+ public static function getSolrUrl($core) {
24+ // Extract extension configuration.
25+ $conf = unserialize($GLOBALS['TYPO3_CONF_VARS']['EXT']['extConf'][tx_dlf_solr::$extKey]);
26+
27+ // Derive Solr host name.
28+ $host = ($conf['solrHost'] ? $conf['solrHost'] : 'localhost');
29+
30+ // Prepend username and password to hostname.
31+ if ($conf['solrUser'] && $conf['solrPass']) {
32+ $host = $conf['solrUser'].':'.$conf['solrPass'].'@'.$host;
33+ }
34+
35+ // Set port if not set.
36+ $port = t3lib_div::intInRange($conf['solrPort'], 1, 65535, 8180);
37+
38+ // Append core name to path.
39+ $path = trim($conf['solrPath'], '/').'/'.$core;
40+
41+ // Return entire request url.
42+ return 'http://'.$host.':'.$port.'/'.$path;
43+ }
44+
45+ /**
46 * Get SolrPhpClient service object and establish connection to Solr server
47 * @see EXT:dlf/lib/SolrPhpClient/Apache/Solr/Service.php
48 *
49
50=== modified file 'dlf/ext_autoload.php'
51--- dlf/ext_autoload.php 2012-04-30 16:05:21 +0000
52+++ dlf/ext_autoload.php 2012-06-04 15:53:22 +0000
53@@ -49,6 +49,7 @@
54 'tx_dlf_oai' => $extensionPath.'plugins/oai/class.tx_dlf_oai.php',
55 'tx_dlf_pageview' => $extensionPath.'plugins/pageview/class.tx_dlf_pageview.php',
56 'tx_dlf_search' => $extensionPath.'plugins/search/class.tx_dlf_search.php',
57+ 'tx_dlf_search_suggest' => $extensionPath.'plugins/search/class.tx_dlf_search_suggest.php',
58 'tx_dlf_statistics' => $extensionPath.'plugins/statistics/class.tx_dlf_statistics.php',
59 'tx_dlf_toc' => $extensionPath.'plugins/toc/class.tx_dlf_toc.php',
60 'tx_dlf_toolbox' => $extensionPath.'plugins/toolbox/class.tx_dlf_toolbox.php',
61
62=== modified file 'dlf/ext_emconf.php'
63--- dlf/ext_emconf.php 2012-05-15 08:01:44 +0000
64+++ dlf/ext_emconf.php 2012-06-04 15:53:22 +0000
65@@ -44,6 +44,7 @@
66 'depends' => array(
67 'php' => '5.3.0-',
68 'typo3' => '4.3.0-',
69+ 't3jquery' => '2.1.2-',
70 ),
71 'conflicts' => array(
72 ),
73
74=== modified file 'dlf/ext_localconf.php'
75--- dlf/ext_localconf.php 2012-05-08 07:41:38 +0000
76+++ dlf/ext_localconf.php 2012-06-04 15:53:22 +0000
77@@ -66,4 +66,6 @@
78 // Register command line scripts.
79 $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['GLOBAL']['cliKeys'][$_EXTKEY] = array ('EXT:'.$_EXTKEY.'/cli/class.tx_dlf_cli.php', '_CLI_dlf');
80
81+// Register solr suggest eID.
82+$GLOBALS['TYPO3_CONF_VARS']['FE']['eID_include']['tx_dlf_suggest'] = 'EXT:'.$_EXTKEY.'/plugins/search/class.tx_dlf_search_suggest.php';
83 ?>
84\ No newline at end of file
85
86=== modified file 'dlf/plugins/search/class.tx_dlf_search.php'
87--- dlf/plugins/search/class.tx_dlf_search.php 2012-05-05 11:10:37 +0000
88+++ dlf/plugins/search/class.tx_dlf_search.php 2012-06-04 15:53:22 +0000
89@@ -29,7 +29,7 @@
90 /**
91 * Plugin 'DLF: Search' for the 'dlf' extension.
92 *
93- * @author Sebastian Meyer <sebastian.meyer@slub-dresden.de>
94+ * @author Sebastian Meyer <sebastian.meyer@slub-dresden.de>, Henrik Lochmann <dev@mentalmotive.com>
95 * @copyright Copyright (c) 2011, Sebastian Meyer, SLUB Dresden
96 * @package TYPO3
97 * @subpackage tx_dlf
98@@ -39,6 +39,103 @@
99
100 public $scriptRelPath = 'plugins/search/class.tx_dlf_search.php';
101
102+ /**
103+ * Adds the HTML content for two hidden input fields, namely 'encrypted' and 'hashed', containing
104+ * the index_name if the passed solrCore number, to the passed marker arrays value
105+ * '###ADDITIONAL_INPUTS###'. This is necessary to savely render search suggestions.
106+ *
107+ * @access protected
108+ *
109+ * @return array
110+ */
111+ protected function addEncryptedSolrCore($markerArray, $solrCore) {
112+
113+ // Extract internal dlf core name.
114+ $table = 'tx_dlf_solrcores';
115+
116+ $_result = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
117+ $table.'.index_name AS index_name',
118+ $table,
119+ $table.'.uid='.$solrCore.tx_dlf_helper::whereClause($table),
120+ '',
121+ '',
122+ '1'
123+ );
124+
125+ $dlfCoreName = '';
126+
127+ if ($GLOBALS['TYPO3_DB']->sql_num_rows($_result) > 0) {
128+
129+ $resArray = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($_result);
130+
131+ $dlfCoreName = $resArray['index_name'];
132+
133+ }
134+
135+ if (empty($dlfCoreName)) {
136+
137+ trigger_error('Failed to query for internal solr core name of core number '.$solrCore, E_USER_NOTICE);
138+
139+ return;
140+
141+ }
142+
143+ $encryptedCore = tx_dlf_search_suggest::encrypt($dlfCoreName);
144+
145+ if (empty($encryptedCore)) {
146+
147+ $markerArray['###ADDITIONAL_INPUTS###'] = '';
148+
149+ } else {
150+
151+ $markerArray['###ADDITIONAL_INPUTS###'] = '<input type="hidden" name="encrypted" value="'.$encryptedCore['value'].'"/>'
152+ .'<input type="hidden" name="hashed" value="'.$encryptedCore['hash'].'"/>';
153+
154+ }
155+
156+ return $markerArray;
157+
158+ }
159+
160+ /**
161+ * Adds the JS files necessary for search sugestions to the
162+ * page header.
163+ *
164+ * @access protected
165+ *
166+ * @return void
167+ */
168+ protected function addSuggestSupport() {
169+ // ensure jquery is loaded
170+ if (t3lib_extMgm::isLoaded('t3jquery')) {
171+ require_once(t3lib_extMgm::extPath('t3jquery').'class.tx_t3jquery.php');
172+ }
173+
174+ // if t3jquery is loaded and the custom Library had been created
175+ if (T3JQUERY === true) {
176+ tx_t3jquery::addJqJS();
177+ } else {
178+ /*
179+ * suggestions will not work; we don't indicate an no error
180+ * because this is caused by insufficient configuration and
181+ * this feature does not harm the rest of the framework's
182+ * functionality
183+ */
184+ return;
185+ }
186+
187+ $libs = array(
188+ "search_suggest" => "search_suggest.js"
189+ );
190+
191+ foreach ($libs as $lib_key => $lib_file) {
192+ $GLOBALS['TSFE']->additionalHeaderData[$this->prefixId.$lib_key] = ' <script type="text/javascript" src="'
193+ .t3lib_extMgm::siteRelPath($this->extKey)
194+ .'plugins/search/'.$lib_file
195+ .'"></script>';
196+ }
197+ }
198+
199 /**
200 * The main method of the PlugIn
201 *
202@@ -58,15 +155,18 @@
203
204 // Quit without doing anything if required variables are not set.
205 if (empty($this->conf['solrcore'])) {
206-
207+
208 trigger_error('Incomplete configuration for plugin '.get_class($this), E_USER_NOTICE);
209
210 return $content;
211
212 }
213-
214+
215 if (empty($this->piVars['query'])) {
216-
217+
218+ // Add suggest JavaScript file.
219+ $this->addSuggestSupport();
220+
221 // Load template file.
222 if (!empty($this->conf['templateFile'])) {
223
224@@ -97,7 +197,10 @@
225 '###FIELD_QUERY###' => $this->prefixId.'[query]',
226 '###QUERY###' => htmlspecialchars($lastQuery),
227 );
228-
229+
230+ // Encrypt solr core name and add as hidden input field to marker array to enable for search suggestions.
231+ $markerArray = $this->addEncryptedSolrCore($markerArray, $this->conf['solrcore']);
232+
233 // Display search form.
234 $content .= $this->cObj->substituteMarkerArray($this->template, $markerArray);
235
236
237=== added file 'dlf/plugins/search/class.tx_dlf_search_suggest.php'
238--- dlf/plugins/search/class.tx_dlf_search_suggest.php 1970-01-01 00:00:00 +0000
239+++ dlf/plugins/search/class.tx_dlf_search_suggest.php 2012-06-04 15:53:22 +0000
240@@ -0,0 +1,140 @@
241+<?php
242+/***************************************************************
243+ * Copyright notice
244+ *
245+ * (c) 2012 Henrik Lochmann <dev@mentalmotive.com>
246+ * All rights reserved
247+ *
248+ * This script is part of the TYPO3 project. The TYPO3 project is
249+ * free software; you can redistribute it and/or modify
250+ * it under the terms of the GNU General Public License as published by
251+ * the Free Software Foundation; either version 2 of the License, or
252+ * (at your option) any later version.
253+ *
254+ * The GNU General Public License can be found at
255+ * http://www.gnu.org/copyleft/gpl.html.
256+ *
257+ * This script is distributed in the hope that it will be useful,
258+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
259+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
260+ * GNU General Public License for more details.
261+ *
262+ * This copyright notice MUST APPEAR in all copies of the script!
263+ ***************************************************************/
264+
265+/**
266+ * Search suggestion Ajax backend for the Plugin 'DLF: Search' of the
267+ * 'dlf' extension. This class is invoked using the eID bypass.
268+ *
269+ * @author Henrik Lochmann <dev@mentalmotive.com>
270+ * @copyright Copyright (c) 2012, Zeutschel GmbH
271+ * @package TYPO3
272+ * @subpackage tx_dlf
273+ * @access public
274+ */
275+require_once (PATH_tslib . 'class.tslib_pibase.php');
276+class tx_dlf_search_suggest extends tslib_pibase {
277+
278+ private $content;
279+
280+ private static $ENCRYPTION_KEY = 'b8b311560d3e6f8dea0aa445995b1b2b';
281+
282+ private static $ENCRYPTION_IV = '381416de30a5c970f8f486aa6d5cc932';
283+
284+ public $scriptRelPath = 'plugins/search/class.tx_dlf_search_suggest.php';
285+
286+ public static function encrypt($original_value) {
287+
288+ if (!extension_loaded('mcrypt')) {
289+
290+ trigger_error('Mycrpt PHP extension not installed, falling back to TSFE instantiation for '.get_class(), E_USER_NOTICE);
291+
292+ return NULL;
293+
294+ }
295+
296+ $iv = substr( self::$ENCRYPTION_IV, 0, mcrypt_get_iv_size(MCRYPT_BLOWFISH, MCRYPT_MODE_CFB) );
297+
298+ $encryptedValue = base64_encode( mcrypt_encrypt(MCRYPT_BLOWFISH, self::$ENCRYPTION_KEY, $original_value, MCRYPT_MODE_CFB, $iv) );
299+
300+ $salt = substr( md5(uniqid(rand(), true)), 0, 10 );
301+
302+ $hash = $salt . substr(sha1($salt . $original_value),-10);
303+
304+ return array( 'value' => $encryptedValue, 'hash' => $hash);
305+
306+ }
307+
308+ public static function decrypt($encrypted, $hash) {
309+
310+ if (!extension_loaded('mcrypt')) {
311+
312+ trigger_error('Mycrpt PHP extension not installed, falling back to TSFE instantiation for '.get_class(), E_USER_NOTICE);
313+
314+ return NULL;
315+
316+ }
317+
318+ if (empty($encrypted) || empty($hash)) {
319+
320+ return NULL;
321+
322+ }
323+
324+ $result = NULL;
325+
326+ $iv = substr(self::$ENCRYPTION_IV, 0, mcrypt_get_iv_size(MCRYPT_BLOWFISH, MCRYPT_MODE_CFB));
327+
328+ $decrypted_value = mcrypt_decrypt(MCRYPT_BLOWFISH, self::$ENCRYPTION_KEY, base64_decode($encrypted), MCRYPT_MODE_CFB, $iv);
329+
330+ $salt = substr($hash, 0, 10);
331+
332+ $new_hashed_value = $salt . substr(sha1($salt . $decrypted_value), -10);
333+
334+ if ($new_hashed_value == $hash) {
335+
336+ $result = $decrypted_value;
337+
338+ }
339+
340+ return $result;
341+
342+ }
343+
344+ public function main() {
345+
346+ $core = self::decrypt(t3lib_div::_POST('encrypted'), t3lib_div::_POST('hashed'));
347+
348+ // Mcrypt is not available, solr core did not arrive or was manipulated: give uo here.
349+ if (empty($core)) {
350+
351+ return;
352+
353+ }
354+
355+ $url = trim(tx_dlf_solr::getSolrUrl($core), '/') . '/suggest/?q=' . t3lib_div::_POST('q');
356+
357+ if ($stream = fopen($url, 'r')) {
358+
359+ $this -> content .= stream_get_contents($stream);
360+
361+ fclose($stream);
362+
363+ } else {
364+
365+ $this -> content .= "Could not connect to index server.";
366+
367+ }
368+ }
369+
370+ public function printContent() {
371+ echo $this -> content;
372+ }
373+
374+}
375+
376+$suggest = t3lib_div::makeInstance('tx_dlf_search_suggest');
377+$suggest->main();
378+$suggest->printContent();
379+
380+?>
381\ No newline at end of file
382
383=== added file 'dlf/plugins/search/search_suggest.js'
384--- dlf/plugins/search/search_suggest.js 1970-01-01 00:00:00 +0000
385+++ dlf/plugins/search/search_suggest.js 2012-06-04 15:53:22 +0000
386@@ -0,0 +1,29 @@
387+$(
388+ function(){
389+ // jQuery autocomplete integration
390+ $(".autocomplete").autocomplete({
391+ source: function( request, response ) {
392+ return $.post(
393+ '/',
394+ {
395+ eID: "tx_dlf_suggest",
396+ q: escape(request.term),
397+ encrypted: $("input[name='encrypted']").val(),
398+ hashed: $("input[name='hashed']").val()
399+ },
400+ function( xmlData ) {
401+ var result = new Array();
402+
403+ $('arr[name="suggestion"] str', xmlData).each(function(i) {
404+ if ($(this).text().indexOf(request.term) == 0) {
405+ result.push($(this).text());
406+ }
407+ });
408+
409+ return response(result);
410+ },
411+ 'xml');
412+ }
413+ });
414+ }
415+);
416\ No newline at end of file
417
418=== modified file 'dlf/plugins/search/template.tmpl'
419--- dlf/plugins/search/template.tmpl 2012-03-16 09:06:25 +0000
420+++ dlf/plugins/search/template.tmpl 2012-06-04 15:53:22 +0000
421@@ -1,7 +1,8 @@
422 <!-- ###TEMPLATE### -->
423 <form class="tx-dlf-search-form" action="###ACTION_URL###" method="post" enctype="multipart/form-data">
424 <label for="###FIELD_QUERY###">###LABEL_QUERY###</label>
425- <input type="text" id="###FIELD_QUERY###" name="###FIELD_QUERY###" value="###QUERY###" />
426+ <input type="text" id="###FIELD_QUERY###" name="###FIELD_QUERY###" value="###QUERY###" class="autocomplete" autocomplete="off" role="textbox" aria-autocomplete="list" aria-haspopup="true">
427 <input type="submit" value="###LABEL_SUBMIT###" />
428+ ###ADDITIONAL_INPUTS###
429 </form>
430 <!-- ###TEMPLATE### -->
431\ No newline at end of file
432
433=== added file 'dlf/t3jquery.txt'
434--- dlf/t3jquery.txt 1970-01-01 00:00:00 +0000
435+++ dlf/t3jquery.txt 2012-06-04 15:53:22 +0000
436@@ -0,0 +1,1 @@
437+components=jQuery,Core,Position,Autocomplete
438\ No newline at end of file

Subscribers

People subscribed via source and target branches