blob: 7d99a8de064ac0b0c4741c4bed768c53fb40cd33 [file] [log] [blame]
Nico Huberee52fbc2023-06-24 11:52:57 +00001<?php
2/*
3 * SimpleID
4 *
5 * Copyright (C) Kelvin Mo 2007-9
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the 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 GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public
18 * License along with this program; if not, write to the Free
19 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 *
21 * $Id$
22 */
23
24/**
25 * Functions for persistent storage via the file system.
26 *
27 * In general, there are three different sets of data which SimpleID needs
28 * to store:
29 *
30 * - transient data (e.g. OpenID associations, sessions, auto-login)
31 * - application data (e.g. salt for form tokens)
32 * - user data (e.g. user names, passwords and settings)
33 *
34 * Prior to version 0.7, both transient data and application data are stored
35 * using {@link cache.inc}. From version 0.7, application data are now
36 * stored separately from the cache.
37 *
38 * Prior to version 0.7, user data is only stored in the identity file, to which
39 * SimpleID cannot write. This means that if the user wishes to change a setting,
40 * he or she will need to edit the identity file manually. Other user settings
41 * (e.g. RP preferences) are stored using {@link cache.inc}
42 *
43 * From version 0.7, user data is stored in two files, one is the identity
44 * file, the other is the user store file, which SimpleID can write.
45 *
46 * @package simpleid
47 * @filesource
48 */
49
50/**
51 * This variable is a cache of SimpleID's application settings. It is populated
52 * progressively as {@link store_get()} is called.
53 *
54 * @global array $simpleid_settings
55 */
56$simpleid_settings = array();
57
58/**
59 * Returns whether the user name exists in the user store.
60 *
61 * @param string $uid the name of the user to check
62 * @return bool whether the user name exists
63 */
64function store_user_exists($uid) {
65 if (_store_is_valid_name($uid)) {
66 $identity_file = SIMPLEID_IDENTITIES_DIR . "/$uid.identity";
67 return (file_exists($identity_file));
68 } else {
69 return false;
70 }
71}
72
73/**
74 * Loads user data for a specified user name.
75 *
76 * The user name must exist. You should check whether the user name exists with
77 * the {@link store_user_exists()} function
78 *
79 * @param string $uid the name of the user to load
80 * @return mixed data for the specified user
81 */
82function store_user_load($uid) {
83 if (!_store_is_valid_name($uid)) return array();
84 $store_file = SIMPLEID_STORE_DIR . "/$uid.usrstore";
85
86 if (file_exists($store_file)) {
87 $data = unserialize(file_get_contents($store_file));
88 } else {
89 $data = array();
90 }
91
92 $identity_file = SIMPLEID_IDENTITIES_DIR . "/$uid.identity";
93 $data = array_merge($data, parse_ini_file($identity_file, TRUE));
94
95 return $data;
96}
97
98/**
99 * Returns the time which a user's data has been updated.
100 *
101 * The user name must exist. You should check whether the user name exists with
102 * the {@link store_user_exists()} function.
103 *
104 * The time returned can be based on the identity file,
105 * the user store file, or the latter of the two.
106 *
107 * @param string $uid the name of the user to obtain the update time
108 * @param string $type one of: 'identity' (identity file), 'usrstore' (user store
109 * file) or NULL (latter of the two)
110 * @return int the updated time
111 */
112function store_user_updated_time($uid, $type = NULL) {
113 if (!_store_is_valid_name($uid)) return NULL;
114
115 $identity_file = SIMPLEID_IDENTITIES_DIR . "/$uid.identity";
116 $identity_time = filemtime($identity_file);
117
118 $store_file = SIMPLEID_STORE_DIR . "/$uid.usrstore";
119 if (file_exists($store_file)) {
120 $store_time = filemtime($store_file);
121 } else {
122 $store_time = NULL;
123 }
124
125 if ($type == 'identity') {
126 return $identity_time;
127 } elseif ($type == 'usrstore') {
128 return $store_time;
129 } elseif ($type == NULL) {
130 return ($identity_time > $store_time) ? $identity_time : $store_time;
131 } else {
132 return NULL;
133 }
134}
135
136
137/**
138 * Finds the user name from a specified OpenID Identity URI.
139 *
140 * @param string $identity the Identity URI of the user to load
141 * @return string the user name for the Identity URI, or NULL if no user has
142 * the specified Identity URI
143 */
144function store_get_uid($identity) {
145 $uid = cache_get('identity', $identity);
146 if ($uid !== NULL) return $uid;
147
148 $r = NULL;
149
150 $dir = opendir(SIMPLEID_IDENTITIES_DIR);
151
152 while (($file = readdir($dir)) !== false) {
153 $filename = SIMPLEID_IDENTITIES_DIR . '/' . $file;
154
155 if (is_link($filename)) $filename = readlink($filename);
156 if ((filetype($filename) != "file") || (!preg_match('/^(.+)\.identity$/', $file, $matches))) continue;
157
158 $uid = $matches[1];
159 $test_user = store_user_load($uid);
160
161 cache_set('identity', $test_user['identity'], $uid);
162
163 if ($test_user['identity'] == $identity) {
164 $r = $uid;
165 }
166 }
167
168 closedir($dir);
169
170 return $r;
171}
172
173/**
174 * Finds the user name from a specified client SSL certificate string.
175 *
176 * The client SSL certificate string comprises the certificate's serial number
177 * (in capitals hex notation) and the distinguished name of the certificate's issuer
178 * (with components joined using slashes), joined using a semi-colon.
179 *
180 *
181 * @param string $cert the client SSL certificate string of the user to load
182 * @return string the user name matching the client SSL certificate string, or NULL if no user has
183 * client SSL certificate string
184 */
185function store_get_uid_from_cert($cert) {
186 $uid = cache_get('cert', $cert);
187 if ($uid !== NULL) return $uid;
188
189 $r = NULL;
190
191 $dir = opendir(SIMPLEID_IDENTITIES_DIR);
192
193 while (($file = readdir($dir)) !== false) {
194 $filename = SIMPLEID_IDENTITIES_DIR . '/' . $file;
195
196 if (is_link($filename)) $filename = readlink($filename);
197 if ((filetype($filename) != "file") || (!preg_match('/^(.+)\.identity$/', $file, $matches))) continue;
198
199 $uid = $matches[1];
200 $test_user = store_user_load($uid);
201
202 if (isset($test_user['certauth']['cert'])) {
203 if (is_array($test_user['certauth']['cert'])) {
204 foreach ($test_user['certauth']['cert'] as $test_cert) {
205 if (trim($test_cert) != '') cache_set('cert', $test_cert, $uid);
206 }
207 foreach ($test_user['certauth']['cert'] as $test_cert) {
208 if ((trim($test_cert) != '') && ($test_cert == $cert)) $r = $uid;
209 }
210 } else {
211 if (trim($test_cert) != '') {
212 cache_set('cert', $test_user['certauth']['cert'], $uid);
213 if ($test_user['certauth']['cert'] == $cert) $r = $uid;
214 }
215 }
216 }
217 }
218
219 closedir($dir);
220
221 return $r;
222}
223
224/**
225 * Saves user data for a specific user name.
226 *
227 * This data is stored in the user store file.
228 *
229 * @param string $uid the name of the user
230 * @param array $data the data to save
231 * @param array $exclude an array of keys to exclude from the user store file.
232 * These are generally keys which are stored in the identity file.
233 *
234 * @since 0.7
235 */
236function store_user_save($uid, $data, $exclude = array()) {
237 foreach ($exclude as $key) {
238 if (isset($data[$key])) unset($data[$key]);
239 }
240
241 if (!_store_is_valid_name($uid)) {
242 trigger_error("Invalid user name for filesystem store", E_USER_ERROR);
243 return;
244 }
245
246 $store_file = SIMPLEID_STORE_DIR . "/$uid.usrstore";
247 $file = fopen($store_file, 'w');
248 fwrite($file, serialize($data));
249 fclose($file);
250}
251
252/**
253 * Loads an application setting.
254 *
255 * @param string $name the name of the setting to return
256 * @param mixed $default the default value to use if this variable has never been set
257 * @return mixed the value of the setting
258 *
259 */
260function store_get($name, $default = NULL) {
261 global $simpleid_settings;
262
263 if (!_store_is_valid_name($name)) return $default;
264
265 if (!isset($simpleid_settings[$name])) {
266 $setting_file = SIMPLEID_STORE_DIR . "/$name.setting";
267
268 if (file_exists($setting_file)) {
269 $simpleid_settings[$name] = unserialize(file_get_contents($setting_file));
270 } else {
271 return $default;
272 }
273 }
274
275 return $simpleid_settings[$name];
276}
277
278/**
279 * Saves an application setting.
280 *
281 * @param string $name the name of the setting to save
282 * @param mixed $value the value of the setting
283 *
284 */
285function store_set($name, $value) {
286 global $simpleid_settings;
287
288 if (!_store_is_valid_name($name)) {
289 trigger_error("Invalid setting name for filesystem store", E_USER_ERROR);
290 return;
291 }
292
293 $simpleid_settings[$name] = $value;
294
295 $setting_file = SIMPLEID_STORE_DIR . "/$name.setting";
296 $file = fopen($setting_file, 'w');
297 fwrite($file, serialize($value));
298 fclose($file);
299}
300
301/**
302 * Deletes an application setting.
303 *
304 * @param string $name the name of the setting to delete
305 *
306 */
307function store_del($name) {
308 global $simpleid_settings;
309
310 if (!_store_is_valid_name($name)) {
311 trigger_error("Invalid setting name for filesystem store", E_USER_ERROR);
312 return;
313 }
314
315 if (isset($simpleid_settings[$name])) unset($simpleid_settings[$name]);
316
317 $setting_file = SIMPLEID_STORE_DIR . "/$name.setting";
318 if (file_exists($setting_file)) unlink($setting_file);
319}
320
321/**
322 * Determines whether a name is a valid name for use with this store.
323 *
324 * For file system storage, a name is not valid if it contains either a
325 * directory separator (i.e. / or \).
326 *
327 * @param string $name the name to check
328 * @return boolean whether the name is valid for use with this store
329 *
330 */
331function _store_is_valid_name($name) {
332 return preg_match('!\A[^/\\\\]*\z!', $name);
333}
334?>