Completed
Pull Request — master (#386)
by Victor
02:14
created

IndexController::getEndpoint()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 11
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 11
ccs 0
cts 10
cp 0
rs 9.4285
cc 2
eloc 8
nc 1
nop 0
crap 6
1
<?php
2
3
/**
4
 * @author Victor Dubiniuk <[email protected]>
5
 *
6
 * @copyright Copyright (c) 2015, ownCloud, Inc.
7
 * @license AGPL-3.0
8
 *
9
 * This code is free software: you can redistribute it and/or modify
10
 * it under the terms of the GNU Affero General Public License, version 3,
11
 * as published by the Free Software Foundation.
12
 *
13
 * This program is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
 * GNU Affero General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU Affero General Public License, version 3,
19
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
20
 *
21
 */
22
23
namespace Owncloud\Updater\Controller;
24
25
use Owncloud\Updater\Utils\Checkpoint;
26
use Owncloud\Updater\Utils\ConfigReader;
27
use Owncloud\Updater\Formatter\HtmlOutputFormatter;
28
use Owncloud\Updater\Http\Request;
29
use League\Plates\Engine;
30
use League\Plates\Extension\Asset;
31
use League\Plates\Extension\URI;
32
use Symfony\Component\Console\Output\BufferedOutput;
33
use Symfony\Component\Console\Input\StringInput;
34
use Symfony\Component\Process\Exception\ProcessFailedException;
35
36
class IndexController {
37
38
	/** @var \Pimple\Container */
39
	protected $container;
40
41
	/** @var Request */
42
	protected $request;
43
44
	/** @var string $command */
45
	protected $command;
46
47
	/**
48
	 * @param \Pimple\Container $container
49
	 * @param Request|null $request
50
	 */
51
	public function __construct(\Pimple\Container $container,
52
								Request $request = null) {
53
		$this->container = $container;
54
		if (is_null($request)){
55
			$this->request = new Request(['post' => $_POST, 'headers' => $_SERVER]);
56
		} else {
57
			$this->request = $request;
58
		}
59
60
		$this->command = $this->request->postParameter('command');
61
	}
62
63
	public function dispatch() {
64
		/** @var ConfigReader $configReader */
65
		$configReader = $this->container['utils.configReader'];
66
67
		// strip index.php and query string (if any) to get a real base url
68
		$baseUrl = preg_replace('/(index\.php.*|\?.*)$/', '', $_SERVER['REQUEST_URI']);
69
		$templates = new Engine(CURRENT_DIR . '/src/Resources/views/');
70
		$templates->loadExtension(new Asset(CURRENT_DIR . '/pub/', false));
71
		$templates->loadExtension(new URI($baseUrl));
72
		
73
		try {
74
			$fullEndpoint = $this->getEndpoint();
75
			$this->container['application']->setEndpoint($fullEndpoint);
76
			$this->container['application']->setAuthToken($this->request->header('X_Updater_Auth'));
77
			$this->container['application']->assertOwnCloudFound();
78
			$configReader->init();
79
		} catch (\Exception $e){
80
			$content = $templates->render(
81
				'partials/error',
82
				[
83
					'title' => 'Updater',
84
					'version' => $this->container['application']->getVersion(),
85
					'error' => $e->getMessage()
86
				]
87
			);
88
			return $content;
89
		}
90
91
		// Check if the user is logged-in
92
		if(!$this->isLoggedIn()) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->isLoggedIn() of type null|boolean is loosely compared to false; this is ambiguous if the boolean can be false. You might want to explicitly use !== null instead.

If an expression can have both false, and null as possible values. It is generally a good practice to always use strict comparison to clearly distinguish between those two values.

$a = canBeFalseAndNull();

// Instead of
if ( ! $a) { }

// Better use one of the explicit versions:
if ($a !== null) { }
if ($a !== false) { }
if ($a !== null && $a !== false) { }
Loading history...
93
			return $this->showLogin($templates);
94
		}
95
96
		if (is_null($this->command)){
97
			/** @var Checkpoint $checkpoint */
98
			$checkpoint = $this->container['utils.checkpoint'];
99
			$checkpoints = $checkpoint->getAll();
100
			$content = $templates->render(
101
					'partials/inner',
102
					[
103
						'title' => 'Updater',
104
						'version' => $this->container['application']->getVersion(),
105
						'checkpoints' => $checkpoints
106
					]
107
			);
108
		} else {
109
			header('Content-Type: application/json');
110
			$content = json_encode($this->ajaxAction(), JSON_UNESCAPED_SLASHES);
111
		}
112
		return $content;
113
	}
114
115
	/**
116
	 * @return bool
117
	 */
118
	protected function isLoggedIn() {
119
		/** @var ConfigReader $configReader */
120
		$locator = $this->container['utils.locator'];
121
		$storedSecret = $locator->getSecretFromConfig();
122
		if ($storedSecret === '') {
123
			die('updater.secret is undefined in config/config.php. Either browse the admin settings in your ownCloud and click "Open updater" or define a strong secret using <pre>php -r \'echo password_hash("MyStrongSecretDoUseYourOwn!", PASSWORD_DEFAULT)."\n";\'</pre> and set this in the config.php.');
124
		}
125
		$sentAuthHeader = ($this->request->header('X_Updater_Auth') !== null) ? $this->request->header('X_Updater_Auth') : '';
126
127
		if(password_verify($sentAuthHeader, $storedSecret)) {
128
			return true;
129
		}
130
131
		return false;
132
	}
133
134
	public function showLogin(Engine $templates) {
135
		// If it is a request with invalid token just return "false" so that we can catch this
136
		$token = ($this->request->header('X_Updater_Auth') !== null) ? $this->request->header('X_Updater_Auth') : '';
137
		if($token !== '') {
138
			return 'false';
139
		}
140
141
		$content = $templates->render(
142
			'partials/login',
143
			[
144
				'title' => 'Login Required',
145
			]
146
		);
147
		return $content;
148
	}
149
150
	public function ajaxAction() {
151
		$application = $this->container['application'];
152
153
		$input = new StringInput($this->command);
154
		$input->setInteractive(false);
155
156
		$output = new BufferedOutput();
157
		$formatter = $output->getFormatter();
158
		$formatter->setDecorated(true);
159
		$output->setFormatter(new HtmlOutputFormatter($formatter));
160
161
		$application->setAutoExit(false);
162
163
		$fullEndpoint = $this->getEndpoint();
164
		$application->setEndpoint($fullEndpoint);
165
		$application->setAuthToken($this->request->header('X_Updater_Auth'));
166
		// Some commands dump things out instead of returning a value
167
		ob_start();
168
		$errorCode = $application->run($input, $output);
169
		if (!$result = $output->fetch()){
170
			$result = ob_get_contents(); // If empty, replace it by the catched output
171
		}
172
		ob_end_clean();
173
		$result = nl2br($result);
174
		$result = preg_replace('|<br />\r.*<br />(\r.*?)<br />|', '$1<br />', $result);
175
176
		return [
177
			'input' => $this->command,
178
			'output' => $result,
179
			'environment' => '',
180
			'error_code' => $errorCode
181
		];
182
	}
183
	
184
	protected function getEndpoint(){
185
		$endpoint = preg_replace('/(updater\/|updater\/index.php)$/', '', $this->request->getRequestUri());
186
		$fullEndpoint = sprintf(
187
			'%s://%s%sindex.php/occ/',
188
			$this->request->getServerProtocol(),
189
			$this->request->getHost(),
190
			$endpoint !== '' ? $endpoint : '/'
191
		);
192
		
193
		return $fullEndpoint;
194
	}
195
196
}
197