source: trunk/server/www/app/controllers/requests_controller.php @ 294

Last change on this file since 294 was 294, checked in by sander, 10 years ago

Fix access control errors with results that are part of public galleries

File size: 14.1 KB
Line 
1<?php
2/**
3 * Officeshots.org - Test your office documents in different applications
4 * Copyright (C) 2009 Stichting Lone Wolves
5 * Written by Sander Marechal <s.marechal@jejik.com>
6 *
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU Affero General Public License as
9 * published by the Free Software Foundation, either version 3 of the
10 * License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU Affero General Public License for more details.
16 *
17 * You should have received a copy of the GNU Affero General Public License
18 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 */
20
21/**
22 * The Requests controller
23 */
24class RequestsController extends AppController
25{
26        /** @var array The components this controller uses */
27        public $components = array('AuthCert', 'RequestHandler');
28       
29        /** @var array The helpers that will be available on the view */
30        public $helpers = array('Html', 'Form', 'Javascript', 'RequestModel', 'ValidatorModel');
31
32        /** @var array Add Request and Worker model */
33        public $uses = array('Request', 'Worker', 'Mimetype', 'User');
34
35        /** @var array Set default sort order for paginate */
36        public $paginate = array(
37                'order' => array('Request.created' => 'desc')
38        );
39
40        /**
41         * Set the auth permissions for this controller
42         * @return void
43         */
44        public function beforeFilter()
45        {
46                parent::beforeFilter();
47                $this->AuthCert->allow('add', 'view', 'download');
48        }
49
50        /**
51         * List all your requests
52         * @return void
53         */
54        public function index()
55        {
56                $this->set('requests', $this->paginate('Request', array('Request.user_id' => $this->AuthCert->user('id'))));
57        }
58
59        /**
60         * Get a Request by the request ID and check access control
61         *
62         * @param string $id The request ID
63         * @param $type the access type, either 'read' or 'write'
64         * @return array An array containing the request
65         */
66        private function _getRequest($id, $type = 'read')
67        {
68                if (!$id) {
69                        $this->Session->setFlash(__('Invalid Request.', true));
70                        $this->redirect(array('action'=>'add'));
71                }
72               
73                $this->Request->contain(array(
74                        'Format',
75                        'Mimetype',
76                        'Mimetype.Doctype',
77                        'Job',
78                        'Job.Platform',
79                        'Job.Application',
80                        'Job.Result',
81                        'Job.Result.Mimetype',
82                        'Job.Result.Format',
83                        'Job.Result.Validator' => array('order' => 'Validator.name ASC'),
84                        'Gallery',
85                        'Validator' => array('order' => 'Validator.name ASC'),
86                ));
87
88                $request = $this->Request->read(null, $id);
89                if (empty($request) || !$this->Request->checkAccess($this->AuthCert->user('id'), $type, $id)) {
90                        $this->Session->setFlash(__('Invalid Request.', true));
91                        $this->redirect(array('action'=>'add'));
92                }
93
94                return $request;
95        }
96
97        /**
98         * View a single request
99         *
100         * @param string $id The request ID
101         * @return void
102         */
103        public function view($id = null)
104        {
105                // Used to display the jobs associated with the Request
106                $this->helpers[] = 'JobModel';
107
108                $request = $this->_getRequest($id, 'read');
109                $this->set(array(
110                        'request' => $request,
111                        'canDeleteResults' => $this->__permitted('results', 'delete'),
112                ));
113        }
114
115
116        /**
117         * Edit a request description
118         *
119         * @param string $id The request ID
120         * @return void
121         */
122        public function edit($id = null)
123        {
124                $request = $this->_getRequest($id, 'write');
125
126                if (!empty($this->data)) {
127                        $this->data['Request']['id'] = $id;
128                        if (!$this->Request->save($this->data)) {
129                                $this->Session->setFlash(__('Unable to save the description', true));
130                        } else {
131                                $this->redirect(array('action' => 'view', $id));
132                        }
133                }
134
135                $this->set(array(
136                        'request' => $request,
137                ));
138        }
139        /**
140         * Add a new request
141         * @return void
142         */
143        public function add()
144        {
145                if (!empty($this->data)) {
146                        // Access control
147                        if (!Configure::read('Auth.allowAnonymous') && !$this->AuthCert->user()) {
148                                $this->Session->setFlash(__('You are not allowed to submit requests anonymously.', true));
149                                $this->redirect(array('action'=>'add'));
150                        }
151
152                        // Check the daily request limits
153                        if ($user = $this->AuthCert->user()) {
154                                $requestLimit = Configure::read('Request.limitRegistered');
155
156                                $user = $this->User->find(array('User.id' => $user['User']['id']));
157                                $limits = Set::extract('/Group/request_limit', $user);
158                                $limits[] = $requestLimit;
159                                $requestLimit = max($limits);
160
161                                $numRequests = $this->Request->find('count', array(
162                                        'conditions' => array(
163                                                'Request.user_id' => $user['User']['id'],
164                                                'Request.created >' => date('Y-m-d H:i:s', strtotime('-1 day'))
165                                        )
166                                ));
167
168                                if ($numRequests > $requestLimit) {
169                                        $this->Session->setFlash(sprintf(__('You have exceeded your quota of %d daily requests.', true), $requestLimit));
170                                        $this->redirect(array('action'=>'add'));
171                                }
172                        } else {
173                                $requestLimit = Configure::read('Request.limitAnonymous');
174                                $numRequests = $this->Request->find('count', array(
175                                        'conditions' => array(
176                                                'Request.ip_address' => inet_ptod($this->RequestHandler->getClientIP()),
177                                                'Request.created >' => date('Y-m-d H:i:s', strtotime('-1 day'))
178                                        )
179                                ));
180
181                                if ($numRequests > $requestLimit) {
182                                        $this->Session->setFlash(sprintf(__('You have exceeded your quota of %d daily requests.', true), $requestLimit));
183                                        $this->redirect(array('action'=>'add'));
184                                }
185                        }
186
187                        // Check that the user marked any applications
188                        if (!isset($this->data['Request']['App']) || !is_array($this->data['Request']['App']) || sizeof($this->data['Request']['App']) == 0) {
189                                $this->Session->setFlash(__('You did not mark any applications.', true));
190                                $this->redirect(array('action'=>'add'));
191                        }
192
193                        // Set some extra data
194                        $this->data['Request']['user_id']    = (string) $this->AuthCert->user('id');
195                        $this->data['Request']['ip_address'] = inet_ptod($this->RequestHandler->getClientIP());
196                        $this->data['Request']['expire']     = date('Y-m-d H:i:s', time() + Configure::read('Request.expire'));
197                        $this->data['Request']['state']      = Request::STATE_PREPROCESSOR_QUEUED;
198
199
200                        // Create the request
201                        $this->Request->create();
202                        if (!$this->Request->save($this->data)) {
203                                $this->Session->setFlash(__('The request could not be saved', true));
204                                $this->redirect(array('action'=>'add'));
205                        }
206
207                        // Set root and path based on the ID
208                        $this->Request->read();
209                        $this->Request->set(array(
210                                'root' => 'requests' . DS . $this->Request->id,
211                                'path' => 'requests' . DS . $this->Request->id . DS . 'source',
212                        ));
213                       
214                        // Add the file upload
215                        $errors = array();
216                        if (!$this->Request->addUpload($this->data, $errors)) {
217                                $this->Request->delete();
218                                $this->Session->setFlash(implode("<br />\n", $errors));
219                                $this->redirect(array('action'=>'add'));
220                        }
221
222                        // Add the jobs to the request
223                        $jobs = array();
224                        foreach ($this->data['Request']['App'] as $app) {
225                                list($platform_id, $doctype_code, $application_id, $version) = explode('_', $app);
226                                $jobs[] = compact('platform_id', 'application_id', 'version');
227                        }
228
229                        $this->Request->read();
230                        if ($this->Request->addJobs($jobs) == 0) {
231                                $this->Request->delete();
232                                $this->Session->setFlash(__('None of the jobs could be created. The request has been cancelled.', true));
233                                $this->redirect(array('action'=>'add'));
234                        }
235
236                        // Add ODF Validators
237                        $this->Request->addValidators();
238
239                        // Queue the preprocessor
240                        if (!$this->Request->defer('run', 'Preprocessor')) {
241                                $errors[] = __('Failed to queue the request for the virus scanner. Please contact system administration.', true);
242                        }
243
244                        // All done!
245                        $this->redirect(array('action' => 'view', 'id' => $this->Request->id));
246                }
247
248                $this->set('can_have_factories', $this->__permitted('factories', 'edit'));
249                $this->set('can_submit_requests', ($this->__permitted('factories', 'edit') || $this->AuthCert->user()));
250               
251                // TODO: The result of all the below actions should really be cached for better performance
252                $workers = $this->Worker->getActive();
253
254                // Give all the active workers a unique, short ID.
255                // We need this for checking and unchecking sets of applications
256                $count = 0;
257                foreach ($workers as &$worker) {
258                        $worker['short_id'] = 'c'.$count++;
259                }
260                unset($worker); // Don't leave it assigned or bugs will occur below
261
262                // Get all the other information we need to build the front page
263                $platforms = $this->Worker->Factory->Operatingsystem->Platform->find('all');
264                $doctypes = $this->Worker->Application->Doctype->find('all');
265                $formats = $this->Request->Format->find('list');
266                $mimetypes = $this->Mimetype->find('all');
267
268                // Build a format map which consists of sets of active applications
269                $sets = array(
270                        'platform'    => array(),
271                        'extension'   => array(),
272                        'format'      => array(),
273                        'latest'      => array(),
274                        'development' => Set::extract('/Worker[development=1]/../short_id', $workers),
275                        'all'         => Set::extract('/short_id', $workers)
276                );
277
278                foreach ($platforms as $platform) {
279                        $sets['platform'][$platform['Platform']['id']] = Set::extract('/Platform[id=' . $platform['Platform']['id'] . ']/../short_id', $workers);
280                }
281                foreach ($mimetypes as $mimetype) {
282                        if ($mimetype['Mimetype']['doctype_id']) {
283                                $sets['extension'][$mimetype['Mimetype']['extension']] = Set::extract('/Doctype[id=' . $mimetype['Mimetype']['doctype_id'] . ']/../short_id', $workers);
284                        }
285                }
286                foreach ($formats as $format_id => $format) {
287                        $sets['format'][$format_id] = Set::extract('/Format[id=' . $format_id . ']/../short_id', $workers);
288                }
289
290                // This is the tricky bit. Set the highest version for every application/platform combo as default
291                // This requires the workers array to be sorted by platform, app, version
292                $short_id = false;
293                $platform_id = '';
294                $application_id = '';
295                foreach ($workers as $worker) {
296                        if ($worker['Worker']['development']) {
297                                continue;
298                        }
299
300                        if ($worker['Application']['id'] != $application_id || $worker['Platform']['id'] != $platform_id) {
301                                if ($short_id) {
302                                        $sets['latest'][] = $short_id;
303                                }
304
305                                $platform_id = $worker['Platform']['id'];
306                                $application_id = $worker['Application']['id'];
307                        }
308
309                        $short_id = $worker['short_id'];
310                }
311                $sets['latest'][] = $short_id; // don't forget to add the last one.
312
313                $this->set(compact('workers', 'platforms', 'doctypes', 'formats', 'sets', 'mimetypes'));
314        }
315
316        /**
317         * Download a request
318         *
319         * @param string $id The request ID
320         */
321        public function download($id = null)
322        {
323                $request = $this->_getRequest($id, 'read');
324                extract($this->Request->createZip());
325               
326                $this->view = 'Media';
327                $this->set(array(
328                        'id' => $id,
329                        'name' => $filename,
330                        'download' => true,
331                        'extension' => 'zip',
332                        'path' => $directory . DS,
333                ));
334        }
335
336        /**
337         * Extend the deadline of a request
338         *
339         * @param string $id The request ID
340         */
341        public function extend($id = null)
342        {
343                $request = $this->_getRequest($id, 'write');
344
345                if (strtotime($request['Request']['expire']) < time()) {
346                        $this->Session->setFlash(__('You cannot extend a request that has expired.', true));
347                        $this->redirect(array('action' => 'view', $id));
348                }
349
350                $this->Request->saveField('expire', date('Y-m-d H:i:s', time() + Configure::read('Request.expire')));
351                $this->redirect(array('action' => 'view', $id));
352        }
353
354        /**
355         * Cancel a job by setting it's expiration time
356         *
357         * @param string $id The request ID
358         */
359        public function cancel($id = null)
360        {
361                $request = $this->_getRequest($id, 'write');
362                $this->Request->cancel();
363                $this->redirect(array('action' => 'view', $id));
364        }
365
366        /**
367         * List all requests
368         * @return void
369         */
370        public function admin_index()
371        {
372                $this->Request->recursive = 0;
373                $this->set('requests', $this->paginate());
374        }
375
376        /**
377         * View a single request
378         *
379         * @param string $id The request ID
380         * @return void
381         */
382        public function admin_view($id = null)
383        {
384                if (!$id) {
385                        $this->Session->setFlash(__('Invalid Request.', true));
386                        $this->redirect(array('action'=>'index'));
387                }
388                $this->set('request', $this->Request->read(null, $id));
389        }
390
391        /**
392         * Add a new request manually
393         * @return void
394         */
395        public function admin_add()
396        {
397                if (!empty($this->data)) {
398                        $this->data['Request']['ip_address'] = inet_ptod($this->data['Request']['ip_address']);
399                        $this->Request->create();
400                        if ($this->Request->save($this->data)) {
401                                $this->Session->setFlash(__('The Request has been saved', true));
402                                $this->redirect(array('action'=>'index'));
403                        } else {
404                                $flash = $this->Request->Behaviors->File->errors;
405                                array_unshift($flash, __('The Request could not be saved. Please, try again.', true));
406                                $this->Session->setFlash(implode('<br />', $flash));
407                        }
408                }
409                $users = $this->Request->User->find('list');
410                $mimetypes = $this->Request->Mimetype->find('list');
411                $formats = $this->Request->Format->find('list');
412                $this->set(compact('users','mimetypes','formats'));
413        }
414
415        /**
416         * Edit a request
417         *
418         * @param string $id The request ID
419         * @return void
420         */
421        public function admin_edit($id = null)
422        {
423                if (!$id && empty($this->data)) {
424                        $this->Session->setFlash(__('Invalid Request', true));
425                        $this->redirect(array('action'=>'index'));
426                }
427                if (!empty($this->data)) {
428                        $this->data['Request']['ip_address'] = inet_ptod($this->data['Request']['ip_address']);
429                        if ($this->Request->save($this->data)) {
430                                $this->Session->setFlash(__('The Request has been saved', true));
431                                $this->redirect(array('action'=>'index'));
432                        } else {
433                                $flash = $this->Request->Behaviors->File->errors;
434                                array_unshift($flash, __('The Request could not be saved. Please, try again.', true));
435                                $this->Session->setFlash(implode('<br />', $flash));
436                        }
437                }
438                if (empty($this->data)) {
439                        $this->data = $this->Request->read(null, $id);
440                        $this->data['Request']['ip_address'] = inet_dtop($this->data['Request']['ip_address']);
441                }
442                $users = $this->Request->User->find('list');
443                $mimetypes = $this->Request->Mimetype->find('list');
444                $formats = $this->Request->Format->find('list');
445                $this->set(compact('users','mimetypes','formats'));
446        }
447
448        /**
449         * Delete a request
450         *
451         * @param string $id The request ID
452         * @return void
453         */
454        public function admin_delete($id = null)
455        {
456                if (!$id) {
457                        $this->Session->setFlash(__('Invalid id for Request', true));
458                        $this->redirect(array('action'=>'index'));
459                }
460                if ($this->Request->del($id)) {
461                        $this->Session->setFlash(__('Request deleted', true));
462                        $this->redirect(array('action'=>'index'));
463                }
464        }
465}
466
467?>
Note: See TracBrowser for help on using the repository browser.