users._pike
Go to the documentation of this file.
1 /* Copyright (C) 2000-2006 Thomas Bopp, Thorsten Hampel, Ludger Merkens
2  *
3  * This program is free software; you can redistribute it and/or modify
4  * it under the terms of the GNU General Public License as published by
5  * the Free Software Foundation; either version 2 of the License, or
6  * (at your option) any later version.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program; if not, write to the Free Software
15  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16  *
17  * $Id: users.pike,v 1.2 2010/01/25 19:18:18 astra Exp $
18  */
19 inherit "/kernel/secure_mapping.pike";
20 #include <macros.h>
21 #include <database.h>
22 #include <classes.h>
23 #include <attributes.h>
24 //! This module keeps track of the users in the database. -
25 //! Therefor it maps a nickname to the related sTeam user object.
26 class users : public secure_mapping.pike{
27 public:
28 
29 
30 
31 
32 
33 
34 private function myfDb;
35 private string mysDbTable;
36 private array test_objects = ({ });
37 
38 void load_module()
39 {
40  ::load_module();
41  [myfDb , mysDbTable] = _Database->connect_db_mapping();
42  if( search(myfDb()->list_tables(), "i_userlookup" ) == -1 ) {
43  MESSAGE("Creating i_userlookup table in users module.");
44 #if 0
45  array types = ({ ([ "name": "login", "type":"char(255) not null", ]),
46  ([ "name": "email", "type":"char(255)", ]),
47  ([ "name": "firstname", "type":"char(255)", ]),
48  ([ "name": "lastname", "type":"char(255)", ]),
49  ([ "name": "user_id", "type":"char(255)", ]),
50  ([ "name": "ob_id", "type":"char(255) not null",
51  "unique":"true"]) });
52 
53  myfDb()->create_table("i_userlookup", types);
54 #endif
55  mixed err = catch {
56  myfDb()->big_query("create table i_userlookup "
57  "(login char(255) not null, email char(255), firstname char(255), lastname char(255), user_id char(255), ob_id char(255) not null, UNIQUE(ob_id))");
58 
59  foreach( get_users(), object u) {
60  update_user(u);
61  }
62  };
63  if ( err != 0 )
64  FATAL("Failed to create userlookup table: %O\n%O", err[0],err[1]);
65  }
66 }
67 
68 int unregister_user(object u)
69 {
70  WARN("users module: unregister from invalid %O", CALLER);
71  THROW("Unauthorized call to users module: unregister()", E_ACCESS);
72  }
73  unregister(u->get_user_name());
74 }
75 
76 protected:
77  int unregister(string key)
78 {
79  int result = ::unregister(key);
80  // delete this user from i_userlookup, too
81  myfDb()->big_query("delete from i_userlookup where login='"+
82  myfDb()->quote_index(key) + "'");
83  return result;
84 }
85 
86 public:
87 
88 
89 void update_user(object u)
90 {
91  if ( objectp(u) ) {
92  WARN("users module: update_user from invalid %O", CALLER);
93  THROW("Unauthorized call to users module: update_user!", E_ACCESS);
94  }
95  Sql.sql_result res = myfDb()->big_query("select * from i_userlookup where ob_id='"+u->get_object_id()+"'");
96  mixed err = catch {
97  string query;
98  if ( res->fetch_row() ) {
99  query = sprintf("update i_userlookup set login='%s',email='%s',firstname='%s',lastname='%s',user_id='%s' where ob_id='%s'",
100  myfDb()->quote(u->get_user_name()||""),
101  myfDb()->quote(u->query_attribute(USER_EMAIL)||""),
102  myfDb()->quote(u->query_attribute(USER_FIRSTNAME)||""),
103  myfDb()->quote(u->query_attribute(USER_FULLNAME)||""),
104  myfDb()->quote(u->query_attribute(USER_ID)||""),
105  (string)u->get_object_id());
106  }
107  else {
108  query =
109  sprintf("insert into i_userlookup values('%s','%s','%s','%s','%s','%s')",
110  myfDb()->quote(u->get_user_name()||""),
111  myfDb()->quote(u->query_attribute(USER_EMAIL)||""),
112  myfDb()->quote(u->query_attribute(USER_FIRSTNAME)||""),
113  myfDb()->quote(u->query_attribute(USER_FULLNAME)||""),
114  myfDb()->quote(u->query_attribute(USER_ID)||""),
115  (string)u->get_object_id());
116  }
117  myfDb()->big_query(query);
118  };
119  if ( err ) {
120  FATAL("Failed to insert user: %O\n%O", err[0], err[1]);
121  }
122  }
123 }
124 
125 int register(string uname, object user)
126 {
127  WARN("users module: register from invalid %O", CALLER);
128  THROW("Unauthorized call to users module: register()", E_ACCESS);
129  }
130  mixed err = catch(update_user(user));
131  if ( err )
132  FATAL("While updating user: %O\n%O", err[0], err[1]);
133 
134  return ::register(uname, user);
135 }
136 
137 /**
138  * Lookup a user by his login
139  *
140  * @param string login the login of the user to search
141  * @param bool like optional parameter to specify "like" instead of "="
142  * @return array of matching users
143  */
144 array lookup_login(string login, void|bool like)
145 {
146  return _Persistence->lookup_users( ([ "login":login ]), false,
147  (like ? "*" : 0) );
148 }
149 
150 /**
151  * Lookup a user by his name (firstname, lastname)
152  *
153  * @param string firstname the firstname of the user to search
154  * @param string lastname the last name of the user to search
155  * @param bool like optional parameter to specify "like" instead of "="
156  * @return array of matching users
157  */
158 array lookup_name(string firstname, string lastname, void|bool like)
159 {
160  return _Persistence->lookup_users( ([ "firstname":firstname,
161  "lastname":lastname ]), false,
162  (like ? "*" : 0) );
163 }
164 
165 array search_name(string firstname, string lastname, void|bool like)
166 {
167  return _Persistence->lookup_users( ([ "firstname":firstname,
168  "lastname":lastname ]), true,
169  (like ? "*" : 0) );
170 }
171 
172 /**
173  * Lookup a user by his lastname
174  *
175  * @param string lastname the last name of the user to search
176  * @return array of matching users
177  */
178 array lookup_lastname(string lastname, void|bool like)
179 {
180  return _Persistence->lookup_users( ([ "lastname":lastname ]), false,
181  (like ? "*" : 0) );
182 }
183 
184 /**
185  * Lookup a user by his first name
186  *
187  * @param string firstname the first name of the user to search
188  * @return array of matching users
189  */
190 array lookup_firstname(string firstname, void|bool like)
191 {
192  return _Persistence->lookup_users( ([ "firstname":firstname ]), false,
193  (like ? "*" : 0) );
194 }
195 
196 /**
197  * Search users by a term
198  *
199  * @param string term the search term
200  * @return array of matching users
201  */
202 array search_users(string term, void|bool like)
203 {
204  return _Persistence->lookup_users( ([ "firstname":term, "lastname":term,
205  "login":term, "email":term ]), true,
206  (like ? "*" : 0) );
207 }
208 
209 int check_read_attribute(object user, string attribute)
210 {
211  mixed err = catch(user->check_read_attribute(attribute,
212  geteuid()||this_user()));
213  if ( err ) {
214  if ( sizeof(err) == 3)
215  return 0;
216  FATAL("Error while checking for readable attribute %O of %O\n%O:%O",
217  attribute, user, err[0], err[1]);
218  }
219  return 1;
220 }
221 
222 protected:
223  array check_user(int id, void|string|array attribute)
224 {
225  // check if user data are still ok ?!
226  object user = find_object(id);
227  if (attribute) {
228  if ( arrayp(attribute) ) {
229  foreach(attribute, string a)
230  if ( check_read_attribute(user, a) == 0 )
231  return ({ });
232  }
233  else
234  if ( check_read_attribute(user, attribute) == 0 )
235  return ({ });
236  }
237  return ({ user });
238 }
239 
240 public:
241 
242 
243 /**
244  * Lookup a user by id
245  *
246  * @param string id the id to lookup
247  * @return array of matching users
248  */
249 array lookup_id(string id)
250 {
251  Sql.sql_result res = myfDb()->big_query("select ob_id from i_userlookup where user_id='"+id+"'");
252 
253  mixed row;
254 
255  if ( !objectp(res) )
256  return 0;
257 
258  array result = ({ });
259  while ( row = res->fetch_row() ) {
260  result += check_user((int)row[0], USER_ID);
261  }
262  destruct(res);
263  return result;
264 }
265 
266 /**
267  * Lookup a user by e-mail
268  *
269  * @param string e-mail the e-mail to lookup
270  * @param bool like exact search flag
271  * @return array of matching users
272  */
273 array lookup_email(string email, void|bool like)
274 {
275  if (!stringp(email) || strlen(email) == 0)
276  return ({ });
277 
278  return _Persistence->lookup_users( ([ "email":email ]), false,
279  (like ? "*" : 0) );
280 }
281 
282 /**
283  * Lookup a user by login
284  *
285  * @param string index the login to lookup
286  * @return matching user
287  */
288 object lookup(string index)
289 {
290  return _Persistence->lookup_user(index);
291 }
292 
293 string rename_user(object user, string new_name)
294 {
295  if ( CALLER != get_factory(CLASS_USER) )
296  steam_error("Invalid call to rename_user() !");
297  object other = get_value(new_name);
298  if ( objectp(other) && other != user )
299  steam_error("There is already a user with the name '"+new_name+"' !");
300  string name = user->get_user_name();
301  delete( name );
302  set_value(new_name, user);
303  _Persistence->user_renamed( user, name, new_name );
304  return new_name;
305 }
306 
307 array get_users()
308 {
309  array index = index();
310  array users = ({ });
311 
312  foreach ( index, string idx ) {
313  object obj = get_value(idx);
314  if ( objectp(obj) )
315  users += ({ obj });
316  }
317  return users;
318 }
319 
320 object get_user(string name )
321 {
322  return get_value(name);
323 }
324 
325 /**
326  * Drops a user from any caches, so that it will receive fresh data on the
327  * next lookup. This can be useful if you know that user data has changed in
328  * one of the persistence layers and you want the user object to update its
329  * data accordingly (before the regular update after the cache times out).
330  *
331  * @param identifier the identifier (user's login name) of the user that shall
332  * be dropped from cache
333  * @return 1 if the user was dropped from the object cache,
334  * 0 if it wasn't found in the object cache (regardless of this return value,
335  * the user might have been dropped from any persistence layer caches)
336  */
337 int uncache_user ( string identifier )
338 {
339  return _Persistence->uncache_user( identifier );
340 }
341 
342 int check_updates () {
343  int result = 0; // set to 1 if an update needs a server restart
344 
345  // sent-mail update:
346  string sent_mail_update = "activated sent-mail for all users";
347  if ( !_Server->get_update( sent_mail_update ) ) {
348  array users = get_users();
349  int nr_users = sizeof(users);
350  MESSAGE( "Activating sent-mail for all %d users...", nr_users );
351  string update_log = "=Sent-Mails=\n\n" + ctime(time())
352  + sprintf( "\nCreating sent-mail folders and activating sent-mail "
353  + "storage for all %d users.\n\n", nr_users );
354  int count = 0;
355  int failed = 0;
356  foreach ( users, object user ) {
357  if ( user == _GUEST ) continue; // skip guest user
358  string username = user->get_identifier();
359  update_log += "* " + username + " : folder ";
360  mixed res;
361  mixed err = catch( res = user->create_sent_mail_folder() );
362  if ( err ) {
363  update_log += "'''could not be created''' (" + err[0] + "), storage ";
364  werror("Could not create sent-mail folder for user "+username+"\n");
365  failed++;
366  }
367  else if ( !objectp(res) ) {
368  update_log += "'''could not be created''', storage ";
369  werror("Could not create sent-mail folder for user "+username+"\n");
370  failed++;
371  }
372  else
373  update_log += "created, storage ";
374  err = catch( res = user->set_is_storing_sent_mail( 1 ) );
375  if ( err ) {
376  update_log += "'''could not be activated''' (" + err[0] + ")\n";
377  werror("Could not activate sent-mail storage for user "+username+"\n");
378  failed++;
379  }
380  else if ( !user->is_storing_sent_mail() ) {
381  update_log += "'''could not be activated'''\n";
382  werror("Could not activate sent-mail storage for user "+username+"\n");
383  failed++;
384  }
385  else
386  update_log += "activated\n";
387  count++;
388  if ( (count % 100) == 0 )
389  MESSAGE("Activated sent-mails for %d of %d users...", count, nr_users);
390  }
391  update_log += "\n" + ctime(time()) + "\n";
392  update_log += "\n" + failed + " errors occurred.\n";
393  object update = get_factory(CLASS_DOCUMENT)->execute(
394  ([ "name":sent_mail_update, "mimetype":"text/wiki" ]) );
395  if ( objectp(update) ) {
396  update->set_content( update_log );
397  _Server->add_update( update );
398  MESSAGE( "Finished activating sent-mail for users." );
399  }
400  else {
401  MESSAGE( "Failed to store sent-mail update." );
402  werror( "Failed to store sent-mail update.\n" );
403  }
404  }
405 
406  return result;
407 }
408 
409 string get_identifier() { return "users"; }
410 string get_table_name() { return "users"; }
411 
412 {
413  object factory = get_factory(CLASS_USER);
414 
415  string uname;
416  object user;
417  int uname_count = 1;
418  do {
419  uname = "test_" + ((string)time()) + "_" + ((string)uname_count++);
420  user = USER( uname );
421  } while ( objectp(user) );
422  user = factory->execute( (["name": uname, "pw":"test", "email": "xyz",]) );
423  if ( objectp(user) ) test_objects += ({ user });
424 
425  //try to find
426  array users = lookup_email("xyz");
427  Test.test("User searching 1", search(users, user) >= 0);
428  Test.test("User lookup", lookup(uname) == user);
429 
430  user->set_attribute(USER_FIRSTNAME, "xyzz");
431  users = lookup_firstname("xyzz");
432  Test.test("User searching 2", search(users, user) >= 0);
433 }
434 
435 void test_cleanup () {
436  if ( arrayp(test_objects) ) {
437  foreach ( test_objects, object obj )
438  catch( obj->delete() );
439  }
440 }
441 
442 
443 };