groups._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: groups.pike,v 1.2 2010/01/26 12:09:01 astra Exp $
18  */
19 inherit "/kernel/secure_mapping.pike";
20 #include <macros.h>
21 #include <attributes.h>
22 #include <classes.h>
23 #include <events.h>
24 #include <database.h>
25 //! This module maps the name of the group to the group object.
26 //! Its possible to get a list of all groups inside here. Apart
27 //! from that its only used by the server directly.
28 class groups : public secure_mapping.pike{
29 public:
30 
31 
32 
33 
34 
35 
36 private array test_objects = ({ });
37 
38 /**
39  * Get a list of groups.
40  *
41  * @return an array of groups.
42  */
43 array get_groups()
44 {
45  array index = index();
46  array groups = ({ });
47 
48 
49  foreach ( index, string idx ) {
50  object obj = get_value(idx);
51  if ( objectp(obj) )
52  groups += ({ obj });
53  }
54  return groups;
55 }
56 
57 array get_top_groups()
58 {
59  array groups = do_query_attribute("groups_top_groups") || ({ });
60  groups -= ({ 0 });
61  return groups;
62 }
63 
64 void unregister_group(object grp)
65 {
66  WARN("groups module: unregister from invalid %O", CALLER);
67  THROW("Unauthorized call to groups module: unregister()", E_ACCESS);
68  }
69  unregister(grp->get_group_name());
70 }
71 
72 protected:
73  int unregister(string groupName)
74 {
75  return ::unregister(groupName);
76 }
77 
78 public:
79 
80 int register(string name, object obj)
81 {
82  WARN("groups module: register from invalid %O", CALLER);
83  THROW("Unauthorized call to groups module: register()", E_ACCESS);
84  }
85  if (!objectp(obj->get_parent())) {
86  MESSAGE("New top level group %O", obj);
87  array topgroups = get_top_groups();
88  topgroups += ({ obj });
89  do_set_attribute("groups_top_groups", topgroups);
90  }
91  return ::register(name, obj);
92 }
93 
94 void rename_group(object group, string new_name)
95 {
96  if ( CALLER != get_factory(CLASS_GROUP) )
97  steam_error("Invalid call to rename_group() !");
98  string old_name = group->get_group_name();
99  set_value(new_name, group);
100  if ( stringp(old_name) && new_name != old_name ) delete( old_name );
101 }
102 
103 /**
104  * Initialize the module. Only sets the description attribute.
105  *
106  */
107 private:
108 void init_module()
109 {
110  set_attribute(OBJ_NAME, "groups");
111  set_attribute(OBJ_DESC, "This is the database table for lookup "+
112  "of Groups !");
113 }
114 
115 public:
116 
117 private:
118 void init_groups()
119 {
120  array topgroups = ({ });
121  array groups = get_groups();
122  foreach(groups, object grp) {
123  if ( !objectp(grp->get_parent()))
124  topgroups += ({ grp });
125  }
126  do_set_attribute("groups_top_groups", topgroups);
127 }
128 
129 public:
130 
131 
132 protected:
133  void load_module()
134 {
135  ::load_module();
136  array topgroups = do_query_attribute("groups_top_groups");
137  if ( !arrayp(topgroups) )
138  init_groups();
139  add_global_event(EVENT_ADD_MEMBER, event_add_member, PHASE_NOTIFY);
140  add_global_event(EVENT_REMOVE_MEMBER, event_remove_member, PHASE_NOTIFY);
141 }
142 
143 public:
144 
145 void event_add_member(int e, object grp, object caller, object add, bool pw)
146 {
147  array topgroups = get_top_groups();
148  if ( search(topgroups, add) >= 0 ) {
149  topgroups -= ({ add });
150  do_set_attribute("groups_top_groups", topgroups);
151  }
152 }
153 
154 void event_remove_member(int e, object grp, object caller, object user)
155 {
156  array topgroups = get_top_groups();
157  if ( user->get_object_class() & CLASS_GROUP ) {
158  if ( !objectp(user->get_parent()) ) {
159  topgroups += ({ user });
160  do_set_attribute("groups_top_groups", topgroups);
161  }
162  }
163 }
164 
165 object lookup(string index)
166 {
167  return _Persistence->lookup_group(index);
168 }
169 
170 
171 /**
172  * Lookup a group by name
173  *
174  * @param string name the name of the group to search
175  * @param bool like optional parameter to specify "like" instead of "="
176  * @return array of matching groups
177  */
178 array lookup_name(string name, void|bool like)
179 {
180  return _Persistence->lookup_groups( ([ "name":name ]), false,
181  (like ? "*" : 0) );
182 }
183 
184 
185 object get_group ( string name )
186 {
187  return get_value(name);
188 }
189 
190 /**
191  * Drops a group from any caches, so that it will receive fresh data on the
192  * next lookup. This can be useful if you know that group data has changed in
193  * one of the persistence layers and you want the group object to update its
194  * data accordingly (before the regular update after the cache times out).
195  *
196  * @param identifier the identifier (full group name with parent groups
197  * separated by ".") of the group that shall be dropped from cache
198  * @return 1 if the group was dropped from the object cache,
199  * 0 if it wasn't found in the object cache (regardless of this return value,
200  * the group might have been dropped from any persistence layer caches)
201  */
202 int uncache_group ( string identifier )
203 {
204  return _Persistence->uncache_group( identifier );
205 }
206 
207 string get_identifier() { return "groups"; }
208 string get_table_name() { return "groups"; }
209 
210 void check_integrity()
211 {
212  array topgroups = get_top_groups();
213  foreach (topgroups, object grp) {
214  if ( objectp(grp) )
215  if ( grp->get_parent() )
216  steam_error("Found Top-Group with Parent !");
217  }
218  array groups = get_groups();
219  foreach(groups, object grp) {
220  if ( !objectp(grp->get_parent()) && search(topgroups, grp) == -1 ) {
221  register(grp->get_group_name(), grp);
222  steam_error("Found Top-Group %O not in get_top_groups() ... fixed!",
223  grp->get_object());
224  }
225  }
226 }
227 
228 int check_updates () {
229  int result = 0; // set to 1 if an update needs a server restart
230 
231  // parent update:
232  string parent_update = "fixed parents of all groups";
233  if ( !_Server->get_update( parent_update ) ) {
234  array groups = get_groups();
235  int nr_groups = sizeof(groups);
236  MESSAGE( "Fixing parents for all %d groups...", nr_groups );
237  string update_log = "=Fixing-Parents=\n\n" + ctime(time())
238  + sprintf( "\nFixing parents of all %d groups.\n\n", nr_groups );
239  int count = 0;
240  int failed = 0;
241  object group_factory = get_factory( CLASS_GROUP );
242  foreach ( groups, object group ) {
243  count++;
244  if ( (count % 100) == 0 )
245  MESSAGE("Fixed parent for %d of %d groups...", count, nr_groups);
246  string identifier = group->get_identifier();
247  if ( !stringp(identifier) ) {
248  update_log += "* '''ERROR:''' skipping group with empty identifier "
249  + "(id: " + group->get_object_id() + ")\n";
250  werror( "group with empty identifier: %O\n", group );
251  failed++;
252  continue;
253  }
254  object old_parent = group->get_parent();
255  if ( objectp(old_parent) ) {
256  mixed old_parent_identifier = old_parent->get_identifier();
257  if ( !stringp(old_parent_identifier) )
258  old_parent_identifier = "(invalid identifier)";
259  update_log += "* " + identifier + " : already has parent: "
260  + old_parent_identifier + " (id: " + old_parent->get_object_id()
261  + ")\n";
262  continue;
263  }
264  object new_parent = group_factory->fix_group_parent( group );
265  if ( !objectp(new_parent) ) {
266  if ( !arrayp(group->get_groups()) || sizeof(group->get_groups())==0 ) {
267  update_log += "* "+identifier+" : is top-level group, skipping\n";
268  continue;
269  }
270  update_log += "* '''ERROR:''' "+identifier+" : could not be fixed\n";
271  werror( "Failed to fix group parent of %O\n", group );
272  failed++;
273  continue;
274  }
275  mixed new_parent_identifier = new_parent->get_identifier();
276  if ( !stringp(new_parent_identifier) )
277  new_parent_identifier = "(empty identifier)";
278  if ( new_parent != group->get_parent() ) {
279  update_log += "* '''ERROR:''' " + identifier + " : parent could not be"
280  + " set: " + new_parent_identifier + " (id: "
281  + new_parent->get_object_id() + ")\n";
282  werror( "Could not set parent %O to group %O\n", new_parent, group );
283  failed++;
284  continue;
285  }
286  update_log += "* " + identifier + " : new parent: "
287  + new_parent_identifier + " (id: "+new_parent->get_object_id()+")\n";
288  }
289  update_log += "\n" + ctime(time()) + "\n";
290  update_log += "\n" + failed + " errors occurred.\n";
291  object update = get_factory(CLASS_DOCUMENT)->execute(
292  ([ "name":parent_update, "mimetype":"text/wiki" ]) );
293  if ( objectp(update) ) {
294  update->set_content( update_log );
295  _Server->add_update( update );
296  MESSAGE( "Finished fixing group parents." );
297  }
298  else {
299  MESSAGE( "Failed to store group parent update." );
300  werror( "Failed to store group parent update.\n" );
301  }
302  }
303 
304  return result;
305 }
306 
307 {
308  object oldgroup = GROUP("groupstestgroup");
309  if ( objectp(oldgroup) )
310  catch( oldgroup->delete() );
311  oldgroup = GROUP("groupstestgroup.groupstestgroup");
312  if ( objectp(oldgroup) )
313  catch( oldgroup->delete() );
314  oldgroup = GROUP("Groups.groupstestgroup");
315  if ( objectp(oldgroup) )
316  catch( oldgroup->delete() );
317 
318  check_integrity();
319  // new top level group
320  object factory = get_factory(CLASS_GROUP);
321  object grp = factory->execute( ([ "name": "groupstestgroup", ]) );
322  if ( objectp(grp) ) test_objects += ({ grp });
323  Test.test( "creating new top-level group",
324  ( search(get_top_groups(), grp) != -1 ) );
325  object grp2 = factory->execute( ([ "name": "groupstestgroup",
326  "parentgroup": grp ]) );
327  if ( objectp(grp2) ) test_objects += ({ grp2 });
328  Test.test( "creating sub-group",
329  ( search(get_top_groups(), grp2) == -1 ),
330  "found sub-group in top-level groups" );
331 
332  // now move
333  Test.test( "add_member",
334  GROUP("Groups")->add_member(grp) == 1,
335  "Cannot Add Group to Groups");
336  Test.add_test_function(test_more, 1, grp, grp2);
337 }
338 
339 protected:
340  void test_more(object grp, object grp2)
341 {
342  Test.test( "moving group",
343  (grp->get_parent() == GROUP("Groups")),
344  "Moved groups parent is not Groups (is "+
345  sprintf("%O", grp->get_parent()) + ")");
346 
347  Test.test( "top groups",
348  ( search(get_top_groups(), grp) == -1 ),
349  "Found moved group (" + grp->get_group_name()+
350  ") in top-level groups" );
351 }
352 
353 public:
354 
355 void test_cleanup () {
356  if ( arrayp(test_objects) ) {
357  foreach ( test_objects, object obj )
358  catch( obj->delete() );
359  }
360 }
361 
362 
363 };