|
1
|
|
|
<?php |
|
2
|
|
|
|
|
3
|
|
|
/* * ******************************************************************************** |
|
4
|
|
|
* (c) 2011-15 GÉANT on behalf of the GN3, GN3plus and GN4 consortia |
|
5
|
|
|
* License: see the LICENSE file in the root directory |
|
6
|
|
|
* ********************************************************************************* */ |
|
7
|
|
|
?> |
|
8
|
|
|
<?php |
|
9
|
|
|
|
|
10
|
|
|
/** |
|
11
|
|
|
* This file contains the DBConnection singleton. |
|
12
|
|
|
* |
|
13
|
|
|
* @author Stefan Winter <[email protected]> |
|
14
|
|
|
* @author Tomasz Wolniewicz <[email protected]> |
|
15
|
|
|
* |
|
16
|
|
|
* @package Developer |
|
17
|
|
|
*/ |
|
18
|
|
|
require_once('Helper.php'); |
|
19
|
|
|
require_once('Profile.php'); |
|
20
|
|
|
require_once('IdP.php'); |
|
21
|
|
|
|
|
22
|
|
|
/** |
|
23
|
|
|
* This class is a singleton for establishing a connection to the database |
|
24
|
|
|
* |
|
25
|
|
|
* @author Stefan Winter <[email protected]> |
|
26
|
|
|
* @author Tomasz Wolniewicz <[email protected]> |
|
27
|
|
|
* |
|
28
|
|
|
* @license see LICENSE file in root directory |
|
29
|
|
|
* |
|
30
|
|
|
* @package Developer |
|
31
|
|
|
*/ |
|
32
|
|
|
class DBConnection { |
|
33
|
|
|
|
|
34
|
|
|
/** |
|
35
|
|
|
* This is the actual constructor for the singleton. It creates a database connection if it is not up yet, and returns a handle to the database connection on every call. |
|
36
|
|
|
* @return DBConnection the (only) instance of this class |
|
37
|
|
|
*/ |
|
38
|
|
|
private static function handle($db) { |
|
39
|
|
|
switch (strtoupper($db)) { |
|
40
|
|
|
case "INST": |
|
41
|
|
|
if (!isset(self::$instance_inst)) { |
|
42
|
|
|
$c = __CLASS__; |
|
43
|
|
|
self::$instance_inst = new $c($db); |
|
44
|
|
|
} |
|
45
|
|
|
return self::$instance_inst; |
|
46
|
|
|
case "USER": |
|
47
|
|
|
if (!isset(self::$instance_user)) { |
|
48
|
|
|
$c = __CLASS__; |
|
49
|
|
|
self::$instance_user = new $c($db); |
|
50
|
|
|
} |
|
51
|
|
|
return self::$instance_user; |
|
52
|
|
|
case "EXTERNAL": |
|
53
|
|
|
if (!isset(self::$instance_external)) { |
|
54
|
|
|
$c = __CLASS__; |
|
55
|
|
|
self::$instance_external = new $c($db); |
|
56
|
|
|
} |
|
57
|
|
|
return self::$instance_external; |
|
58
|
|
|
default: |
|
59
|
|
|
return FALSE; |
|
|
|
|
|
|
60
|
|
|
} |
|
61
|
|
|
} |
|
62
|
|
|
|
|
63
|
|
|
/** |
|
64
|
|
|
* Implemented for safety reasons only. Cloning is forbidden and will tell the user so. |
|
65
|
|
|
*/ |
|
66
|
|
|
public function __clone() { |
|
67
|
|
|
trigger_error('Clone is not allowed.', E_USER_ERROR); |
|
68
|
|
|
} |
|
69
|
|
|
|
|
70
|
|
|
/** |
|
71
|
|
|
* |
|
72
|
|
|
* @param string $db The database to do escapting for |
|
73
|
|
|
* @param string $value The value to escape |
|
74
|
|
|
* @return string |
|
75
|
|
|
*/ |
|
76
|
|
|
public static function escape_value($db, $value) { |
|
77
|
|
|
$handle = DBConnection::handle($db); |
|
78
|
|
|
debug(5, "Escaping $value for DB $db to get a safe query value.\n"); |
|
79
|
|
|
$escaped = mysqli_real_escape_string($handle->connection, $value); |
|
80
|
|
|
debug(5, "This is the result: $escaped .\n"); |
|
81
|
|
|
return $escaped; |
|
82
|
|
|
} |
|
83
|
|
|
|
|
84
|
|
|
/** |
|
85
|
|
|
* executes a query and triggers logging to the SQL audit log if it's not a SELECT |
|
86
|
|
|
* @param string $querystring the query to be executed |
|
87
|
|
|
* @return mixed the query result as mysqli_result object; or TRUE on non-return-value statements |
|
88
|
|
|
*/ |
|
89
|
|
|
public static function exec($db, $querystring) { |
|
90
|
|
|
// log exact query to debug log, if log level is at 5 |
|
91
|
|
|
debug(5, "DB ATTEMPT: " . $querystring . "\n"); |
|
92
|
|
|
|
|
93
|
|
|
$instance = DBConnection::handle($db); |
|
94
|
|
|
if ($instance->connection == FALSE) { |
|
95
|
|
|
debug(1, "ERROR: Cannot send query to $db database (no connection)!"); |
|
96
|
|
|
return FALSE; |
|
97
|
|
|
} |
|
98
|
|
|
|
|
99
|
|
|
$result = mysqli_query($instance->connection, $querystring); |
|
|
|
|
|
|
100
|
|
|
if ($result == FALSE) { |
|
101
|
|
|
debug(1, "ERROR: Cannot execute query in $db database - (hopefully escaped) query was '$querystring'!"); |
|
102
|
|
|
return FALSE; |
|
103
|
|
|
} |
|
104
|
|
|
|
|
105
|
|
|
// log exact query to audit log, if it's not a SELECT |
|
106
|
|
|
if (preg_match("/^SELECT/i", $querystring) == 0) { |
|
107
|
|
|
CAT::writeSQLAudit("[DB: " . strtoupper($db) . "] " . $querystring); |
|
108
|
|
|
} |
|
109
|
|
|
return $result; |
|
110
|
|
|
} |
|
111
|
|
|
|
|
112
|
|
|
/** |
|
113
|
|
|
* Retrieves data from the underlying tables, for situations where instantiating the IdP or Profile object is inappropriate |
|
114
|
|
|
* |
|
115
|
|
|
* @param type $table institution_option or profile_option |
|
116
|
|
|
* @param type $row rowindex |
|
117
|
|
|
* @return boolean |
|
118
|
|
|
*/ |
|
119
|
|
|
public static function fetchRawDataByIndex($table, $row) { |
|
120
|
|
|
// only for select tables! |
|
121
|
|
|
if ($table != "institution_option" && $table != "profile_option" && $table != "federation_option") { |
|
122
|
|
|
return FALSE; |
|
123
|
|
|
} |
|
124
|
|
|
$blob_query = DBConnection::exec("INST", "SELECT option_value from $table WHERE row = $row"); |
|
125
|
|
|
while ($a = mysqli_fetch_object($blob_query)) { |
|
126
|
|
|
$blob = $a->option_value; |
|
127
|
|
|
} |
|
128
|
|
|
if (!isset($blob)) { |
|
129
|
|
|
return FALSE; |
|
130
|
|
|
} |
|
131
|
|
|
return $blob; |
|
132
|
|
|
} |
|
133
|
|
|
|
|
134
|
|
|
/** |
|
135
|
|
|
* Checks if a raw data pointer is public data (return value FALSE) or if |
|
136
|
|
|
* yes who the authorised admins to view it are (return array of user IDs) |
|
137
|
|
|
*/ |
|
138
|
|
|
public static function isDataRestricted($table, $row) { |
|
139
|
|
|
if ($table != "institution_option" && $table != "profile_option" && $table != "federation_option") { |
|
140
|
|
|
return []; // better safe than sorry: that's an error, so assume nobody is authorised to act on that data |
|
141
|
|
|
} |
|
142
|
|
|
switch ($table) { |
|
143
|
|
|
case "profile_option": |
|
144
|
|
|
$blob_query = DBConnection::exec("INST", "SELECT profile_id from $table WHERE row = $row"); |
|
145
|
|
|
while ($profileIdQuery = mysqli_fetch_object($blob_query)) { // only one row |
|
146
|
|
|
$blobprofile = $profileIdQuery->profile_id; |
|
147
|
|
|
} |
|
148
|
|
|
// is the profile in question public? |
|
149
|
|
|
$profile = new Profile($blobprofile); |
|
|
|
|
|
|
150
|
|
|
if ($profile->getShowtime() == TRUE) { // public data |
|
|
|
|
|
|
151
|
|
|
return FALSE; |
|
152
|
|
|
} else { |
|
153
|
|
|
$inst = new IdP($profile->institution); |
|
154
|
|
|
return $inst->owner(); |
|
155
|
|
|
} |
|
156
|
|
|
break; |
|
|
|
|
|
|
157
|
|
|
case "institution_option": |
|
158
|
|
|
$blob_query = DBConnection::exec("INST", "SELECT institution_id from $table WHERE row = $row"); |
|
159
|
|
|
while ($instIdQuery = mysqli_fetch_object($blob_query)) { // only one row |
|
160
|
|
|
$blobinst = $instIdQuery->institution_id; |
|
161
|
|
|
} |
|
162
|
|
|
$inst = new IdP($blobinst); |
|
|
|
|
|
|
163
|
|
|
// if at least one of the profiles belonging to the inst is public, the data is public |
|
164
|
|
|
if ($inst->isOneProfileShowtime()) { // public data |
|
165
|
|
|
return FALSE; |
|
166
|
|
|
} else { |
|
167
|
|
|
return $inst->owner(); |
|
168
|
|
|
} |
|
169
|
|
|
break; |
|
|
|
|
|
|
170
|
|
|
case "federation_option": |
|
171
|
|
|
// federation metadata is always public |
|
172
|
|
|
return FALSE; |
|
173
|
|
|
default: |
|
174
|
|
|
return []; // better safe than sorry: that's an error, so assume nobody is authorised to act on that data |
|
175
|
|
|
} |
|
176
|
|
|
} |
|
177
|
|
|
|
|
178
|
|
|
/** |
|
179
|
|
|
* Retrieves the last auto-id of an INSERT. Needs to be called immediately after the corresponding exec() call |
|
180
|
|
|
* @return int the last autoincrement-ID |
|
181
|
|
|
*/ |
|
182
|
|
|
public static function lastID($db) { |
|
183
|
|
|
$instance = DBConnection::handle($db); |
|
184
|
|
|
return mysqli_insert_id($instance->connection); |
|
185
|
|
|
} |
|
186
|
|
|
|
|
187
|
|
|
/** |
|
188
|
|
|
* Holds the singleton instance reference |
|
189
|
|
|
* |
|
190
|
|
|
* @var DBConnection |
|
191
|
|
|
*/ |
|
192
|
|
|
private static $instance_user; |
|
193
|
|
|
private static $instance_inst; |
|
194
|
|
|
private static $instance_external; |
|
195
|
|
|
|
|
196
|
|
|
/** |
|
197
|
|
|
* The connection to the DB server |
|
198
|
|
|
* |
|
199
|
|
|
* @var mysqli |
|
200
|
|
|
*/ |
|
201
|
|
|
private $connection; |
|
202
|
|
|
|
|
203
|
|
|
/** |
|
204
|
|
|
* Class constructor. Cannot be called directly; use handle() |
|
205
|
|
|
*/ |
|
206
|
|
|
private function __construct($db) { |
|
207
|
|
|
$DB = strtoupper($db); |
|
208
|
|
|
$this->connection = mysqli_connect(Config::$DB[$DB]['host'], Config::$DB[$DB]['user'], Config::$DB[$DB]['pass'], Config::$DB[$DB]['db']) or die("ERROR: Unable to connect to $DB database! This is a fatal error, giving up."); |
|
209
|
|
|
if ($this->connection == FALSE) { |
|
210
|
|
|
echo "ERROR: Unable to connect to $db database! This is a fatal error, giving up."; |
|
211
|
|
|
exit(1); |
|
212
|
|
|
} |
|
213
|
|
|
|
|
214
|
|
|
if ($db == "EXTERNAL" && Config::$CONSORTIUM['name'] == "eduroam" && isset(Config::$CONSORTIUM['deployment-voodoo']) && Config::$CONSORTIUM['deployment-voodoo'] == "Operations Team") |
|
215
|
|
|
mysqli_query($this->connection, "SET NAMES 'latin1'"); |
|
216
|
|
|
} |
|
217
|
|
|
|
|
218
|
|
|
} |
If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.
Let’s take a look at an example:
Our function
my_functionexpects aPostobject, and outputs the author of the post. The base classPostreturns a simple string and outputting a simple string will work just fine. However, the child classBlogPostwhich is a sub-type ofPostinstead decided to return anobject, and is therefore violating the SOLID principles. If aBlogPostwere passed tomy_function, PHP would not complain, but ultimately fail when executing thestrtouppercall in its body.