UserFactory._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: UserFactory.pike,v 1.3 2010/02/09 19:33:24 astra Exp $
18  */
19 inherit "/factories/ContainerFactory";
20 #include <macros.h>
21 #include <classes.h>
22 #include <database.h>
23 #include <roles.h>
24 #include <assert.h>
25 #include <events.h>
26 #include <attributes.h>
27 #include <types.h>
28 #include <access.h>
29 class UserFactory : public ContainerFactory{
30 public:
31 
32 
33 
34 
35 
36  int iActivation = 0;
37 
38 private array test_objects = ({ });
39 
40 /**
41  * Initialize the factory with its default attributes.
42  *
43  */
44 private:
45  void init_factory()
46 {
47  ::init_factory();
48 private:
49  init_class_attribute(USER_ADRESS, CMD_TYPE_STRING, "user adress",
50  0, EVENT_ATTRIBUTES_CHANGE, 0,
51  CONTROL_ATTR_USER, "");
52 private:
53  init_class_attribute(USER_MODE, CMD_TYPE_INT, "user mode",
54  0, EVENT_ATTRIBUTES_CHANGE, 0,
55  CONTROL_ATTR_CLIENT, 0);
56 private:
57  init_class_attribute(USER_UMASK, CMD_TYPE_MAPPING, "user umask",
58  EVENT_ATTRIBUTES_QUERY, EVENT_ATTRIBUTES_CHANGE,0,
59  CONTROL_ATTR_USER, ([ ]));
60 private:
61  init_class_attribute(USER_MODE_MSG, CMD_TYPE_STRING,
62  "user mode message", 0,
63  EVENT_ATTRIBUTES_CHANGE, 0,CONTROL_ATTR_USER,"");
64 private:
65  init_class_attribute(USER_EMAIL, CMD_TYPE_STRING, "email",
66  0, EVENT_ATTRIBUTES_CHANGE,0,
67  CONTROL_ATTR_USER, "");
68 private:
69  init_class_attribute(USER_FULLNAME, CMD_TYPE_STRING, "user fullname",0,
70  EVENT_ATTRIBUTES_CHANGE, 0,CONTROL_ATTR_USER, "");
71 private:
72  init_class_attribute(USER_WORKROOM, CMD_TYPE_OBJECT, "workroom", 0,
73  EVENT_ATTRIBUTES_CHANGE, 0,CONTROL_ATTR_USER, 0);
74 private:
75  init_class_attribute(USER_LOGOUT_PLACE, CMD_TYPE_OBJECT, "logout-env",
76  0, EVENT_ATTRIBUTES_CHANGE, 0,
77  CONTROL_ATTR_USER, 0);
78 private:
79  init_class_attribute(USER_LAST_LOGIN, CMD_TYPE_TIME, "last-login",
80  0, EVENT_ATTRIBUTES_CHANGE, 0,
81  CONTROL_ATTR_SERVER, 0);
82 private:
83  init_class_attribute(USER_BOOKMARKROOM, CMD_TYPE_OBJECT, "bookmark room",0,
84  EVENT_ATTRIBUTES_CHANGE, 0,CONTROL_ATTR_USER, 0);
85 private:
86  init_class_attribute(USER_FORWARD_MSG, CMD_TYPE_INT, "forward message",
87  0, EVENT_ATTRIBUTES_CHANGE, 0,
88  CONTROL_ATTR_USER, 1);
89 private:
90  init_class_attribute(USER_FAVOURITES, CMD_TYPE_ARRAY, "favourites list",
91  0, EVENT_ATTRIBUTES_CHANGE, 0,
92  CONTROL_ATTR_USER, ({ }) );
93 
94 public:
95 private:
96  init_class_attribute(USER_CALENDAR, CMD_TYPE_OBJECT, "calendar", 0,
97  EVENT_ATTRIBUTES_CHANGE, 0, CONTROL_ATTR_SERVER,0);
98 private:
99  init_class_attribute(USER_MONITOR, CMD_TYPE_OBJECT, "monitor", 0,
100  EVENT_ATTRIBUTES_CHANGE, 0, CONTROL_ATTR_SERVER,0);
101 private:
102  init_class_attribute(USER_ID, CMD_TYPE_STRING, "user id",
103  EVENT_ATTRIBUTES_QUERY,
104  EVENT_ATTRIBUTES_CHANGE,
105  0,CONTROL_ATTR_USER, "");
106 }
107 
108 /**
109  * Create a new user object with the following vars:
110  * name - the users name (nickname is possible too).
111  * email - the users email adress.
112  * pw - the users initial password.
113  * fullname - the full name of the user.
114  * firstname - the last name of the user.
115  *
116  * @param mapping vars - variables for execution.
117  * @return the objectp of the new user if successfully, or 0 (no access or user
118  * exists)
119  */
120 object execute(mapping vars)
121 {
122  string name;
123  object obj;
124 
125  try_event(EVENT_EXECUTE, CALLER, obj);
126 
127  if ( stringp(vars["nickname"]) )
128  name = string_to_utf8(lower_case(utf8_to_string(vars["nickname"])));
129  else
130  name = vars["name"];
131 
132  obj = MODULE_USERS->lookup(name);
133  if ( objectp(obj) )
134  steam_error("user_create(): User does already exist.");
135  obj = MODULE_GROUPS->lookup(name);
136  if ( objectp(obj) )
137  steam_error("user_create(): Group with this name already exist.");
138 
139  mixed err = catch {
140  object ouid = seteuid(USER("root"));
141  obj = user_create(vars);
142  seteuid(ouid);
143  };
144  if ( err ) {
145  FATAL("Error in UserFactory: %O\n%O", err[0], err[1]);
146  // try to find the user and remove
147  obj = MODULE_USERS->lookup(name);
148  if ( objectp(obj) ) {
149  obj->delete();
150  }
151  throw(err);
152  }
153  run_event(EVENT_EXECUTE, CALLER, obj);
154  return obj;
155 }
156 
157 public:
158 
159 private:
160 private object user_create(mapping vars)
161 {
162  object obj;
163 
164  try_event(EVENT_EXECUTE, CALLER, obj);
165 
166  string name;
167  if ( stringp(vars["nickname"]) )
168  name = lower_case(vars["nickname"]);
169  else
170  name = vars["name"];
171 
172  if ( search(name, " ") >= 0 )
173  steam_error("Whitespaces in Usernames are not allowed");
174 
175  string pw = vars["pw"];
176  string email = vars["email"];
177 
178  if ( stringp(vars->fullname) && !xml.utf8_check(vars->fullname) )
179  steam_error("Failed utf8-check for firstname or fullname !");
180 
181  if ( stringp(vars->firstname) && !xml.utf8_check(vars->firstname) )
182  steam_error("Failed utf8-check for firstname or fullname !");
183 
184  obj = object_create(name, CLASS_NAME_USER, 0, vars["attributes"],
185  vars["attributesAcquired"], vars["attributesLocked"]);
186 
187  function obj_set_attribute = obj->get_function("do_set_attribute");
188  function obj_lock_attribute = obj->get_function("do_lock_attribute");
189  function obj_sanction = obj->get_function("do_sanction_object");
190 
191  obj_lock_attribute(OBJ_NAME);
192  if ( !objectp(obj) ) {
193  SECURITY_LOG("Creation of user " + name + " failed...");
194  return null; // creation failed...
195  }
196 
197  string language;
198  if (stringp(vars["language"]))
199  language=vars["language"];
200 
201  if ( stringp(vars["pw:crypt"]) )
202  obj->set_user_password(vars["pw:crypt"],1);
203  else
204  obj->set_user_password(pw);
205  obj->set_user_name(name);
206  obj_set_attribute(USER_EMAIL, email);
207  obj_set_attribute(USER_FULLNAME, vars["fullname"]);
208  obj_set_attribute(USER_FIRSTNAME, vars["firstname"]);
209 
210  if (stringp(language))
211  obj_set_attribute(USER_LANGUAGE, language);
212  obj->set_creator(_ROOT);
213  obj->set_acquire(0);
214  if ( objectp(this_user()) && this_user() != _GUEST )
215  obj_sanction(this_user(), SANCTION_ALL);
216 
217  if ( stringp(vars["description"]) )
218  obj_set_attribute(OBJ_DESC, vars["description"]);
219  if ( stringp(vars["contact"]) )
220  obj_set_attribute(USER_ADRESS, vars["contact"]);
221 
222  // create sent-mail folder and activate storage of sent-mails:
223  if ( name != "guest" ) {
224  object old_euid = geteuid();
225  seteuid( obj );
226  catch {
227  obj->create_sent_mail_folder();
228  obj->set_is_storing_sent_mail( 1 );
229  };
230  seteuid( old_euid );
231  }
232 
233  object workroom, factory, calendar;
234 
235  factory = _Server->get_factory(CLASS_ROOM);
236 
237  mapping workroomAttributes = ([
238  OBJ_DESC: name + "s workroom", ]);
239 
240  workroom = factory->execute( ([
241  "name":name+"'s workarea",
242  "attributes": workroomAttributes,
243  ]));
244 
245  obj->move(workroom);
246  obj_set_attribute(USER_WORKROOM, workroom);
247  obj_lock_attribute(USER_WORKROOM);
248 
249 
250  object bookmarkroom = factory->execute(([
251  "name":name+"'s bookmarks",
252  ]));
253  obj_set_attribute(USER_BOOKMARKROOM, bookmarkroom);
254  obj_lock_attribute(USER_BOOKMARKROOM);
255 
256 
257  factory = _Server->get_factory(CLASS_TRASHBIN);
258  mapping trashbinAttributes = ([ OBJ_DESC: "Trashbin", ]);
259  object trashbin = factory->execute( ([
260  "name":"trashbin", "attributes":trashbinAttributes,
261  ]));
262  trashbin->set_acquire(0);
263 
264  obj_set_attribute(USER_TRASHBIN, trashbin);
265 
266  calendar = _Server->get_factory(CLASS_CALENDAR)->execute( ([
267  "name":name+"'s calendar", "attributes":calendarAttributes,
268  "attributesLocked": ([ CALENDAR_OWNER: 1, ]),
269  ]) );
270  obj_set_attribute(USER_CALENDAR, calendar);
271  obj_lock_attribute(USER_CALENDAR);
272 
273  // steam users can annotate and read the users attributes.
274  obj_sanction(_STEAMUSER, SANCTION_READ|SANCTION_ANNOTATE);
275 
276  object forwards = get_module("forward");
277  if ( objectp(forwards) ) {
278  string mname = forwards->get_mask_char() + obj->get_user_name();
279  if ( stringp(email) && sizeof(email) > 0 ) {
280  if (forwardErr) {
281  FATAL("Failed to set forward to e-mail %O when creating user %O",
282  email, obj);
283  }
284  }
285  }
286 
287  if ( name != "guest" ) {
288  }
289 
290 
291  if ( !_Persistence->get_dont_create_exits() ) {
292  array inv = workroom->get_inventory_by_class(CLASS_EXIT);
293  if ( sizeof(inv) == 0 ) {
294  factory = _Server->get_factory(CLASS_EXIT);
295  object swa = _STEAMUSER->query_attribute(GROUP_WORKROOM);
296  string exitname = "steam";
297  if ( objectp(swa) )
298  exitname = swa->get_identifier();
299 
300  if ( strlen(exitname) == 0 )
301  exitname = "steam";
302 
303  object exit = factory->execute(([ "name": exitname, "exit_to": swa,]));
304  exit->move(workroom);
305  }
306  }
307 
308  run_event(EVENT_EXECUTE, CALLER, obj);
309 
310  // now remove all guest privileges on this object
311  if ( objectp(_GUEST) ) {
312  obj_sanction(_GUEST, 0);
313  }
314  iActivation = time() + random(100000);
315  obj->set_activation(iActivation);
316 
317 }
318 
319 public:
320 
321 /**
322  * Queries and resets the activation code for an user. Thus
323  * it is required, that the creating object immidiately calls
324  * this function and sends the activation code to the user.
325  *
326  * @return activation code for the last created user
327  */
328 int get_activation()
329 {
330  int res = iActivation;
331  iActivation = 0;
332  return res;
333 }
334 
335 object create_sent_mail_folder ( object user, void|string name )
336 {
337  _SECURITY->check_access(user, CALLER, SANCTION_READ, ROLE_READ_ALL, false);
338 
339  if ( user == _GUEST ) return UNDEFINED;
340  object sent_mail = user->query_attribute( USER_MAIL_SENT );
341  if ( objectp(sent_mail) ) return sent_mail;
342  if ( !stringp(name) ) name = "sent";
343 
344  _SECURITY->check_access(user, CALLER, SANCTION_WRITE, ROLE_WRITE_ALL, false);
345 
346  catch {
347  sent_mail = get_factory( CLASS_CONTAINER )->execute(
348  ([ "name" : name, "attributes" : ([ OBJ_OWNER : user ]),
349  "sanction" : ([ user : SANCTION_ALL, _GUEST:0 ]) ]) );
350  if ( !objectp(sent_mail) )
351  return UNDEFINED;
352  sent_mail->set_creator( user );
353  user->add_annotation( sent_mail );
354  return user->set_sent_mail_folder( sent_mail );
355  };
356  return UNDEFINED;
357 }
358 
359 string rename_user(object user, string new_name)
360 {
361  _SECURITY->check_access(user,CALLER,SANCTION_WRITE,ROLE_WRITE_ALL,false);
362 
363  _Persistence->uncache_object( user );
364 
365  object users = get_module("users");
366 
367  if ( users->lookup(user->get_user_name()) != user )
368  user->set_user_name(new_name);
369 
370  _Persistence->uncache_object( user );
371 
372  // unmount user from home module:
373  int is_mounted = get_module( "home" )->is_mounted( user );
374  if ( is_mounted ) get_module( "home" )->unmount( user );
375 
376  if ( users->rename_user(user, new_name) == new_name )
377  user->set_user_name(new_name);
378 
379  _Persistence->uncache_object( user );
380 
381  // re-mount user in home module:
382  if ( is_mounted ) get_module( "home" )->mount( user );
383 
384  return user->get_user_name();
385 }
386 
387 /**
388  * Finds broken user objects (users with 0 username) and returns the user
389  * names.
390  * @return an array of usernames of broken users
391 */
392 array get_broken_users()
393 {
394  array result = ({ });
395  foreach ( get_module("users")->index(), string uname ) {
396  catch {
397  object user = get_module("users")->lookup(uname);
398  if ( objectp(user) && (user->get_object_class() & CLASS_USER) &&
399  (user->get_user_name()==0 || user->query_attribute(OBJ_NAME)==0) )
400  result += ({ uname });
401  };
402  }
403  return result;
404 }
405 
406 /**
407  * Function that tries to recover broken user objects (users with 0 username).
408  * @param user_names (optional) array of usernames (not user objects!) to
409  * recover. If missing, all broken users will be recovered.
410  * @return an array with the recovered user objects
411  */
412 array recover_users( void|array user_names)
413 {
414  array result = ({ });
415  int t = time();
416  MESSAGE("Starting USER RECOVERY !");
417 
418  if ( !arrayp(user_names) ) {
419  MESSAGE("Checking all users...");
420  user_names = get_broken_users();
421  }
422  else
423  MESSAGE("Checking %d users...", sizeof(user_names));
424 
425  foreach( user_names, mixed uname ) {
426  if ( !stringp(uname) ) continue;
427  mixed err0 = catch {
428  object user = get_module("users")->lookup(uname);
429  if ( objectp(user) && (user->get_object_class() & CLASS_USER) &&
430  user->get_user_name()==0 || user->query_attribute(OBJ_NAME)==0 ) {
431  result += ({ user });
432  user->set_user_name(uname);
433  // heuristics
434  int user_oid = user->get_object_id();
435  mixed err = catch {
436  int oid = user_oid + 1;
437  object obj = find_object( oid++ );
438  if ( objectp(obj) &&
439  ((obj->get_object_class() & CLASS_ROOM) != CLASS_ROOM) ) {
440  // probably the sent-mail folder:
441  obj = find_object( oid++ );
442  }
443  if ( objectp(obj) && (obj->get_object_class() & CLASS_ROOM) )
444  user->set_attribute(USER_WORKROOM, obj);
445  obj = find_object( oid++ );
446  if ( objectp(obj) && (obj->get_object_class() & CLASS_ROOM) )
447  user->set_attribute(USER_BOOKMARKROOM, obj);
448  obj = find_object( oid++ );
449  if ( objectp(obj) && (obj->get_object_class() & CLASS_TRASHBIN) )
450  user->set_attribute(USER_TRASHBIN, obj);
451  obj = find_object( oid++ );
452  if ( objectp(obj) && (obj->get_object_class() & CLASS_CALENDAR) )
453  user->set_attribute(USER_CALENDAR, obj);
454  };
455 
456  array groups = user->get_groups();
457  foreach ( get_module("groups")->get_groups(), object grp ) {
458  if ( grp->is_member(user) && search(groups, grp) == -1 ) {
459  grp->remove_member(user);
460  grp->add_member(user);
461  }
462  }
463  MESSAGE("Recovered user %O", uname );
464  }
465  };
466  if (err0)
467  FATAL("Failed to restore %O\n%O\n%O", uname, err0[0], err0[1]);
468  }
469  MESSAGE("Finished USER RECOVERY in %d seconds", time() - t);
470  return result;
471 }
472 
473 void reset_guest()
474 {
475  USER("guest")->unlock_attribute(OBJ_NAME);
476  USER("guest")->set_attribute(OBJ_NAME, "guest");
477  USER("guest")->set_user_password("guest");
478  USER("guest")->set_user_name("guest");
479  foreach(USER("guest")->get_groups(), object grp) {
480  if ( grp != GROUP("everyone") )
481  grp->remove_member(USER("guest"));
482  }
483 }
484 
485 string get_identifier() { return "User.factory"; }
486 string get_class_name() { return "User"; }
487 int get_class_id() { return CLASS_USER; }
488 
489 
490 {
491  string uname;
492  object user;
493  int uname_count = 1;
494  do {
495  uname = "test_" + ((string)time()) + "_" + ((string)uname_count++);
496  user = USER( uname );
497  } while ( objectp(user) );
498  user = execute( (["name": uname, "pw":"test", "email": "xyz",]) );
499  if ( objectp(user) ) test_objects += ({ user });
500  Test.test( "creating user", objectp(user) );
501 
502  // try to get attributes
503  Test.test("User Attribute e-mail",
504  user->query_attribute(USER_EMAIL) == "xyz");
505  // now try to protect
506  user->add_attribute_reader(USER_EMAIL, user); // only for himself
507  Test.test("User Attribute by Root",
508  user->query_attribute(USER_EMAIL) == "xyz");
509  string uname2;
510  object user2;
511  do {
512  uname2 = "test2_"+ ((string)time()) + "_" + ((string)random(10000));
513  user2 = USER( uname2 );
514  } while ( objectp(user2) );
515  user2 = execute( (["name": uname2, "pw":"test", "email": "xyz",]) );
516  if ( objectp(user2) ) test_objects += ({ user2 });
517  seteuid(user2);
518  Test.test("EUID", geteuid() == user2);
519  mixed err = catch(user->query_attribute(USER_EMAIL));
520  Test.test("Restricted Attribute access denied", err != 0);
521  werror("Looking up by email !\n");
522  array users = get_module("users")->lookup_email("xyz");
523  Test.test("Restricted email lookup fail", search(users, user)==-1);
524  seteuid(0);
525 
526  if ( GROUP("steam") ) {
527  Test.test( "new user is in steam group",
528  GROUP("steam")->is_member( user ) );
529  //if ( !GROUP("steam")->is_member(user) )
530  // steam_error("Failure creating user: no in steam group !");
531  }
532  Test.test( "deleting user", user->delete() );
533  user2->delete();
534  Test.test( "user unregistered ?",
535  !objectp(get_module("users")->lookup(uname)),
536  "Lookup of user still found !");
537 }
538 
539 void test_cleanup () {
540  if ( arrayp(test_objects) ) {
541  foreach ( test_objects, object obj )
542  catch ( obj->delete() );
543  }
544 }
545 
546 
547 };