Completed
Push — master ( 63676d...3faef6 )
by Lukas
28:10 queued 12:28
created

OCI::initialize()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 15
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 10
nc 2
nop 1
dl 0
loc 15
rs 9.4285
c 0
b 0
f 0
1
<?php
2
/**
3
 * @copyright Copyright (c) 2016, ownCloud, Inc.
4
 *
5
 * @author Andreas Fischer <[email protected]>
6
 * @author Bart Visscher <[email protected]>
7
 * @author Joas Schilling <[email protected]>
8
 * @author Jörn Friedrich Dreyer <[email protected]>
9
 * @author Manish Bisht <[email protected]>
10
 * @author Morris Jobke <[email protected]>
11
 * @author Roeland Jago Douma <[email protected]>
12
 * @author Thomas Müller <[email protected]>
13
 * @author Thomas Pulzer <[email protected]>
14
 * @author Victor Dubiniuk <[email protected]>
15
 *
16
 * @license AGPL-3.0
17
 *
18
 * This code is free software: you can redistribute it and/or modify
19
 * it under the terms of the GNU Affero General Public License, version 3,
20
 * as published by the Free Software Foundation.
21
 *
22
 * This program is distributed in the hope that it will be useful,
23
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25
 * GNU Affero General Public License for more details.
26
 *
27
 * You should have received a copy of the GNU Affero General Public License, version 3,
28
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
29
 *
30
 */
31
namespace OC\Setup;
32
33
class OCI extends AbstractDatabase {
34
	public $dbprettyname = 'Oracle';
35
36
	protected $dbtablespace;
37
38
	public function initialize($config) {
39
		parent::initialize($config);
40
		if (array_key_exists('dbtablespace', $config)) {
41
			$this->dbtablespace = $config['dbtablespace'];
42
		} else {
43
			$this->dbtablespace = 'USERS';
44
		}
45
		// allow empty hostname for oracle
46
		$this->dbHost = $config['dbhost'];
47
48
		$this->config->setValues([
49
			'dbhost'		=> $this->dbHost,
50
			'dbtablespace'	=> $this->dbtablespace,
51
		]);
52
	}
53
54
	public function validate($config) {
55
		$errors = array();
56 View Code Duplication
		if(empty($config['dbuser']) && empty($config['dbname'])) {
57
			$errors[] = $this->trans->t("%s enter the database username and name.", array($this->dbprettyname));
58
		} else if(empty($config['dbuser'])) {
59
			$errors[] = $this->trans->t("%s enter the database username.", array($this->dbprettyname));
60
		} else if(empty($config['dbname'])) {
61
			$errors[] = $this->trans->t("%s enter the database name.", array($this->dbprettyname));
62
		}
63
		return $errors;
64
	}
65
66
	public function setupDatabase($username) {
67
		$e_host = addslashes($this->dbHost);
68
		// casting to int to avoid malicious input
69
		$e_port = (int)$this->dbPort;
70
		$e_dbname = addslashes($this->dbName);
71
		//check if the database user has admin right
72 View Code Duplication
		if ($e_host == '') {
73
			$easy_connect_string = $e_dbname; // use dbname as easy connect name
74
		} else {
75
			$easy_connect_string = '//'.$e_host.(!empty($e_port) ? ":{$e_port}" : "").'/'.$e_dbname;
76
		}
77
		$this->logger->debug('connect string: ' . $easy_connect_string, ['app' => 'setup.oci']);
78
		$connection = @oci_connect($this->dbUser, $this->dbPassword, $easy_connect_string);
79
		if(!$connection) {
80
			$errorMessage = $this->getLastError();
81
			if ($errorMessage) {
82
				throw new \OC\DatabaseSetupException($this->trans->t('Oracle connection could not be established'),
83
				$errorMessage.' Check environment: ORACLE_HOME='.getenv('ORACLE_HOME')
84
							.' ORACLE_SID='.getenv('ORACLE_SID')
85
							.' LD_LIBRARY_PATH='.getenv('LD_LIBRARY_PATH')
86
							.' NLS_LANG='.getenv('NLS_LANG')
87
							.' tnsnames.ora is '.(is_readable(getenv('ORACLE_HOME').'/network/admin/tnsnames.ora')?'':'not ').'readable');
88
			}
89
			throw new \OC\DatabaseSetupException($this->trans->t('Oracle username and/or password not valid'),
90
					'Check environment: ORACLE_HOME='.getenv('ORACLE_HOME')
91
							.' ORACLE_SID='.getenv('ORACLE_SID')
92
							.' LD_LIBRARY_PATH='.getenv('LD_LIBRARY_PATH')
93
							.' NLS_LANG='.getenv('NLS_LANG')
94
							.' tnsnames.ora is '.(is_readable(getenv('ORACLE_HOME').'/network/admin/tnsnames.ora')?'':'not ').'readable');
95
		}
96
		//check for roles creation rights in oracle
97
98
		$query='SELECT count(*) FROM user_role_privs, role_sys_privs'
99
			." WHERE user_role_privs.granted_role = role_sys_privs.role AND privilege = 'CREATE ROLE'";
100
		$stmt = oci_parse($connection, $query);
101 View Code Duplication
		if (!$stmt) {
102
			$entry = $this->trans->t('DB Error: "%s"', array($this->getLastError($connection))) . '<br />';
103
			$entry .= $this->trans->t('Offending command was: "%s"', array($query)) . '<br />';
104
			$this->logger->warning($entry, ['app' => 'setup.oci']);
105
		}
106
		$result = oci_execute($stmt);
107
		if($result) {
108
			$row = oci_fetch_row($stmt);
109
110
			if ($row[0] > 0) {
111
				//use the admin login data for the new database user
112
113
				//add prefix to the oracle user name to prevent collisions
114
				$this->dbUser='oc_'.$username;
115
				//create a new password so we don't need to store the admin config in the config file
116
				$this->dbPassword = \OC::$server->getSecureRandom()->generate(30, \OCP\Security\ISecureRandom::CHAR_LOWER.\OCP\Security\ISecureRandom::CHAR_DIGITS);
117
118
				//oracle passwords are treated as identifiers:
119
				//  must start with alphanumeric char
120
				//  needs to be shortened to 30 bytes, as the two " needed to escape the identifier count towards the identifier length.
121
				$this->dbPassword=substr($this->dbPassword, 0, 30);
122
123
				$this->createDBUser($connection);
124
			}
125
		}
126
127
		$this->config->setValues([
128
			'dbuser'		=> $this->dbUser,
129
			'dbname'		=> $this->dbName,
130
			'dbpassword'	=> $this->dbPassword,
131
		]);
132
133
		//create the database not necessary, oracle implies user = schema
134
		//$this->createDatabase($this->dbname, $this->dbuser, $connection);
0 ignored issues
show
Unused Code Comprehensibility introduced by
71% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
135
136
		//FIXME check tablespace exists: select * from user_tablespaces
137
138
		// the connection to dbname=oracle is not needed anymore
139
		oci_close($connection);
140
141
		// connect to the oracle database (schema=$this->dbuser) an check if the schema needs to be filled
142
		$this->dbUser = $this->config->getValue('dbuser');
143
		//$this->dbname = \OC_Config::getValue('dbname');
0 ignored issues
show
Unused Code Comprehensibility introduced by
54% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
144
		$this->dbPassword = $this->config->getValue('dbpassword');
145
146
		$e_host = addslashes($this->dbHost);
147
		$e_dbname = addslashes($this->dbName);
148
149 View Code Duplication
		if ($e_host == '') {
150
			$easy_connect_string = $e_dbname; // use dbname as easy connect name
151
		} else {
152
			$easy_connect_string = '//' . $e_host . (!empty($e_port) ? ":{$e_port}" : "") . '/' . $e_dbname;
153
		}
154
		$connection = @oci_connect($this->dbUser, $this->dbPassword, $easy_connect_string);
155
		if(!$connection) {
156
			throw new \OC\DatabaseSetupException($this->trans->t('Oracle username and/or password not valid'),
157
					$this->trans->t('You need to enter details of an existing account.'));
158
		}
159
		$query = "SELECT count(*) FROM user_tables WHERE table_name = :un";
160
		$stmt = oci_parse($connection, $query);
161
		$un = $this->tablePrefix.'users';
162
		oci_bind_by_name($stmt, ':un', $un);
163 View Code Duplication
		if (!$stmt) {
164
			$entry = $this->trans->t('DB Error: "%s"', array($this->getLastError($connection))) . '<br />';
165
			$entry .= $this->trans->t('Offending command was: "%s"', array($query)) . '<br />';
166
			$this->logger->warning( $entry, ['app' => 'setup.oci']);
167
		}
168
		oci_execute($stmt);
169
	}
170
171
	/**
172
	 * @param resource $connection
173
	 */
174
	private function createDBUser($connection) {
175
		$name = $this->dbUser;
176
		$password = $this->dbPassword;
177
		$query = "SELECT * FROM all_users WHERE USERNAME = :un";
178
		$stmt = oci_parse($connection, $query);
179 View Code Duplication
		if (!$stmt) {
180
			$entry = $this->trans->t('DB Error: "%s"', array($this->getLastError($connection))) . '<br />';
181
			$entry .= $this->trans->t('Offending command was: "%s"', array($query)) . '<br />';
182
			$this->logger->warning($entry, ['app' => 'setup.oci']);
183
		}
184
		oci_bind_by_name($stmt, ':un', $name);
185
		$result = oci_execute($stmt);
186 View Code Duplication
		if(!$result) {
187
			$entry = $this->trans->t('DB Error: "%s"', array($this->getLastError($connection))) . '<br />';
188
			$entry .= $this->trans->t('Offending command was: "%s"', array($query)) . '<br />';
189
			$this->logger->warning($entry, ['app' => 'setup.oci']);
190
		}
191
192
		if(! oci_fetch_row($stmt)) {
193
			//user does not exists let's create it :)
194
			//password must start with alphabetic character in oracle
195
			$query = 'CREATE USER '.$name.' IDENTIFIED BY "'.$password.'" DEFAULT TABLESPACE '.$this->dbtablespace;
196
			$stmt = oci_parse($connection, $query);
197 View Code Duplication
			if (!$stmt) {
198
				$entry = $this->trans->t('DB Error: "%s"', array($this->getLastError($connection))) . '<br />';
199
				$entry .= $this->trans->t('Offending command was: "%s"', array($query)) . '<br />';
200
				$this->logger->warning($entry, ['app' => 'setup.oci']);
201
202
			}
203
			//oci_bind_by_name($stmt, ':un', $name);
0 ignored issues
show
Unused Code Comprehensibility introduced by
73% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
204
			$result = oci_execute($stmt);
205 View Code Duplication
			if(!$result) {
206
				$entry = $this->trans->t('DB Error: "%s"', array($this->getLastError($connection))) . '<br />';
207
				$entry .= $this->trans->t('Offending command was: "%s", name: %s, password: %s',
208
					array($query, $name, $password)) . '<br />';
209
				$this->logger->warning($entry, ['app' => 'setup.oci']);
210
211
			}
212
		} else { // change password of the existing role
213
			$query = "ALTER USER :un IDENTIFIED BY :pw";
214
			$stmt = oci_parse($connection, $query);
215 View Code Duplication
			if (!$stmt) {
216
				$entry = $this->trans->t('DB Error: "%s"', array($this->getLastError($connection))) . '<br />';
217
				$entry .= $this->trans->t('Offending command was: "%s"', array($query)) . '<br />';
218
				$this->logger->warning($entry, ['app' => 'setup.oci']);
219
			}
220
			oci_bind_by_name($stmt, ':un', $name);
221
			oci_bind_by_name($stmt, ':pw', $password);
222
			$result = oci_execute($stmt);
223 View Code Duplication
			if(!$result) {
224
				$entry = $this->trans->t('DB Error: "%s"', array($this->getLastError($connection))) . '<br />';
225
				$entry .= $this->trans->t('Offending command was: "%s"', array($query)) . '<br />';
226
				$this->logger->warning($entry, ['app' => 'setup.oci']);
227
			}
228
		}
229
		// grant necessary roles
230
		$query = 'GRANT CREATE SESSION, CREATE TABLE, CREATE SEQUENCE, CREATE TRIGGER, UNLIMITED TABLESPACE TO '.$name;
231
		$stmt = oci_parse($connection, $query);
232 View Code Duplication
		if (!$stmt) {
233
			$entry = $this->trans->t('DB Error: "%s"', array($this->getLastError($connection))) . '<br />';
234
			$entry .= $this->trans->t('Offending command was: "%s"', array($query)) . '<br />';
235
			$this->logger->warning($entry, ['app' => 'setup.oci']);
236
		}
237
		$result = oci_execute($stmt);
238 View Code Duplication
		if(!$result) {
239
			$entry = $this->trans->t('DB Error: "%s"', array($this->getLastError($connection))) . '<br />';
240
			$entry .= $this->trans->t('Offending command was: "%s", name: %s, password: %s',
241
				array($query, $name, $password)) . '<br />';
242
			$this->logger->warning($entry, ['app' => 'setup.oci']);
243
		}
244
	}
245
246
	/**
247
	 * @param resource $connection
248
	 * @return string
249
	 */
250
	protected function getLastError($connection = null) {
251
		if ($connection) {
252
			$error = oci_error($connection);
253
		} else {
254
			$error = oci_error();
255
		}
256
		foreach (array('message', 'code') as $key) {
257
			if (isset($error[$key])) {
258
				return $error[$key];
259
			}
260
		}
261
		return '';
262
	}
263
}
264