security_cache._pike
Go to the documentation of this file.
1 /* Copyright (C) 2000-2004 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: security_cache.pike,v 1.12 2010/01/26 15:45:39 astra Exp $
18  */
19 inherit "/kernel/secure_mapping";
20 #include <macros.h>
21 #include <exception.h>
22 #include <database.h>
23 #include <access.h>
24 #include <classes.h>
25 //! This module caches security checks in a mapping or the database.
26 //! Instead of traversing the whole group structure it directly
27 //! answers questions like is user A allowed to read document B,
28 //! and document B wont have user A explicitely in its ACL.
29 class security_cache : public secure_mapping{
30 public:
31 
32 
33 
34 
35 
36 
37 mapping mCache =([ ]);
38 int hits=0, total = 0;
39 private function myfDb;
40 
41 void load_module()
42 {
43  string mysDbTable;
44 
45  ::load_module();
46  [myfDb , mysDbTable] = _Database->connect_db_mapping();
47  if( search(myfDb()->list_tables(), "i_cache_security" ) == -1 ) {
48  MESSAGE("Creating i_userlookup table in users module.");
49  mixed err = catch {
50  myfDb()->big_query("create table i_cache_security " +
51  "(obj int not null, user int not null, permissions int not null, "+
52  "UNIQUE(obj, user), INDEX(obj), INDEX(user))");
53 
54  };
55  if ( err != 0 )
56  FATAL("Failed to create security cache table: %O\n%O", err[0],err[1]);
57  }
58 }
59 
60 mixed get_value(mixed idx) {
61  mixed res = mCache[idx];
62  if (res) {
63  return res;
64  }
65  res = ::get_value(idx);
66  mCache[idx] = res;
67  return res;
68 }
69 
70 mixed set_value(mixed idx, mixed val) {
71  mCache[idx] = val;
72  ::set_value(idx, val);
73 }
74 
75 string get_index(mixed obj, mixed user)
76 {
77  if (objectp(obj) && objectp(user)) {
78  return obj->get_object_id() + ":" + user->get_object_id();
79  }
80  return obj + ":" + user;
81 }
82 
83 protected:
84  mixed set_permission(object obj, object user, int permission)
85 {
86  SECURITY_LOG("Cache: Setting permissions: %O, %O, %O\n", obj, user, permission);
87  int oldPermission = get_permission(obj, user);
88  if ( zero_type(oldPermission) ) {
89  myfDb()->big_query(
90  "insert into i_cache_security (obj, user, permissions) values ("+
91  obj->get_object_id() + "," + user->get_object_id() + "," +
92  permission + ")");
93  }
94  else {
95  myfDb()->big_query("update i_cache_security set permissions="+permission+" where " +
96  "user="+user->get_object_id() + " and obj="+obj->get_object_id());
97  }
98  string idx = get_index(obj, user);
99  mCache[idx] = permission;
100  return permission;
101 }
102 
103 public:
104 
105 void add_permission(object obj, object user, int value)
106 {
107  int perm, o_idx;
108 
109  SECURITY_LOG("Cache: Adding permissions: %O, %O, %O\n", obj, user, value);
110 
111  if ( !objectp(obj) ) return;
112 
113  if ( user == _ROOT && value > ( 1<< SANCTION_SHIFT_DENY) )
114  THROW("Odd status of permissions - setting denied permissions for Root-user !", E_ERROR);
115 
116  THROW("No permission to use security cache !", E_ACCESS);
117 
118  // add all dependend objects into the databasese
119  object|function acquire = obj->get_acquire();
120  if ( functionp(acquire) ) acquire = acquire();
121 
122  if ( objectp(acquire ) ) {
123  o_idx = acquire->get_object_id();
124  mixed val = get_value(o_idx);
125  if ( !arrayp(val) )
126  val = ({ });
127  if ( search(val, obj) == -1 ) {
128  set_value(o_idx, val + ({ obj }) );
129  }
130  }
131 
132  perm = get_permission(obj, user);
133  set_permission(obj, user, perm | value);
134 }
135 
136 void remove_permission(object obj)
137 {
138  if ( !objectp(obj) ) return;
139  Sql.sql_result res = myfDb()->big_query("select obj, user from i_cache_security where obj="+ obj->get_object_id());
140 
141  array data;
142  while (data = res->fetch_row()) {
143  string idx = get_index(data[0], data[1]);
144  m_delete(mCache, idx);
145  }
146 
147  array depends;
148  depends = get_value(obj->get_object_id());
149  if ( arrayp(depends) )
150  foreach(depends, object dep)
151  remove_permission(dep);
152 
153  myfDb()->big_query("delete from i_cache_security where obj="+ obj->get_object_id());
154  m_delete(mCache, obj->get_object_id());
155 }
156 
157 void remove_permission_user(object user)
158 {
159  Sql.sql_result res = myfDb()->big_query("select obj, user from i_cache_security where user="+user->get_object_id());
160  array data;
161  while (data = res->fetch_row()) {
162  string idx = get_index(data[0], data[1]);
163  m_delete(mCache, idx);
164  }
165  myfDb()->big_query("delete from i_cache_security where user=" + user->get_object_id());
166 }
167 
168 int get_permission(object obj, object user)
169 {
170  if ( !objectp(obj) || !objectp(user) ) return 0;
171 
172  Sql.sql_result res = myfDb()->big_query("select permissions from i_cache_security where obj="+ obj->get_object_id() + " and user="+user->get_object_id());
173  mixed row = res->fetch_row();
174  if (row) {
175  int permission = (int)row[0];
176  mCache[get_index(obj, user)] = permission;
177  return permission;
178  }
179  return UNDEFINED;
180 }
181 
182 void clear_cache()
183 {
184  if ( GROUP("admin")->is_member(this_user()) ) {
185  clear_table();
186  mCache = ([ ]);
187  return;
188  }
189  steam_error("Unauthorized call to clear_cache()");
190 }
191 
192  object testuser = get_factory(CLASS_USER)->execute( (["name": "security_cache_test_user", "pw":"test", "email": "xyz",]) );
193 
194  seteuid(testuser);
195  object obj2 = get_factory(CLASS_OBJECT)->execute( (["name":"security_cache_obj" ]));
196  Test.test("Correct permissions from security module for simple object test case",
197  _SECURITY->get_user_permissions(obj2, testuser, SANCTION_READ) == SANCTION_READ);
198  seteuid(0);
199 
200  seteuid(USER("root"));
201  Test.test("Correct permissions from security module for simple object test case for user ROOT",
202  _SECURITY->get_user_permissions(obj2, USER("root"), SANCTION_READ) == SANCTION_READ);
203  seteuid(0);
204 
205  object testgroup = get_factory(CLASS_GROUP)->execute( (["name":"security_cache_test_group" ]));
206  seteuid(testuser);
207  catch(testuser->move(testgroup->get_workroom()));
208  // moving a user into a room only required read permissions
209  Test.test("Permissions cached for user NOT member of group ("+
210  get_permission(testgroup->get_workroom(), testuser) +")!",
211  get_permission(testgroup->get_workroom(), testuser) ==
212  (SANCTION_READ << SANCTION_SHIFT_DENY));
213  Test.test("Test User cannot retrieve inventory",
214  catch(testgroup->get_workroom()->get_inventory()) != null);
215  Test.test("Permissions !READ cached for user NOT member of group ("+
216  get_permission(testgroup->get_workroom(), testuser) +")!",
217  get_permission(testgroup->get_workroom(), testuser) ==
218  (SANCTION_READ << SANCTION_SHIFT_DENY));
219  seteuid(0);
220  // now set read permissions
221  testgroup->get_workroom()->sanction_object(testuser, SANCTION_READ);
222  Test.test("Correct permissions from security module for user",
223  _SECURITY->get_user_permissions(testgroup->get_workroom(), testuser, SANCTION_READ) == SANCTION_READ);
224  seteuid(testuser);
225  Test.test("Successfully retrieved inventory!",
226  testgroup->get_workroom()->get_inventory() != null);
227  Test.test("Permissions READ cached for user allowed to read ("+
228  get_permission(testgroup->get_workroom(), testuser) +")!",
229  get_permission(testgroup->get_workroom(), testuser) ==
230  (SANCTION_READ));
231  seteuid(0);
232  // now revoke read permissions
233  testgroup->get_workroom()->sanction_object(testuser, 0);
234  Test.test("Permissions READ cache updated for user NOT allowed to read ("+
235  get_permission(testgroup->get_workroom(), testuser) +")!",
236  get_permission(testgroup->get_workroom(), testuser) == 0);
237  testgroup->add_member(testuser);
238  Test.test("Permissions cached for user ADDED to new group!",
239  get_permission(testgroup->get_workroom(), testuser) == 0,
240  "CACHE="+get_permission(testgroup->get_workroom(), testuser));
241  Test.test("Correct permissions from security module for user member of group",
242  _SECURITY->get_user_permissions(testgroup->get_workroom(), testuser, SANCTION_READ) == SANCTION_READ);
243 
244  seteuid(testuser);
245  // user should be able to access groups workroom now
246  catch(testuser->move(testgroup->get_workroom()));
247  Test.test("Permissions cached for user member of group!",
248  get_permission(testgroup->get_workroom(), testuser) !=
249  (SANCTION_INSERT|SANCTION_READ));
250  seteuid(0);
251  // remove user from group
252  testgroup->remove_member(testuser);
253  Test.test("Permissions cache updated for user removed from group ("+
254  get_permission(testgroup->get_workroom(), testuser) +")!",
255  get_permission(testgroup->get_workroom(), testuser) == 0);
256  object subgroup = get_factory(CLASS_GROUP)->execute( (["name":"security_cache_sub_group" ]));
257  subgroup->add_member(testuser);
258  testgroup->add_member(subgroup);
259  seteuid(testuser);
260  catch(testuser->move(testgroup->get_workroom()));
261  seteuid(0);
262  Test.test("Permissions cached for user member of SUB group!",
263  get_permission(testgroup->get_workroom(), testuser) !=
264  (SANCTION_INSERT|SANCTION_READ));
265 
266  testgroup->remove_member(testuser);
267  Test.test("Permissions cleaned for user member of previous sub group!",
268  get_permission(testgroup->get_workroom(), testuser) == 0,
269  "CACHE="+get_permission(testgroup->get_workroom(), testuser));
270  object subroom = get_factory(CLASS_ROOM)->execute( (["name":"security_cache_sub_room1" ]));
271  subroom->move(subgroup->get_workroom());
272  seteuid(testuser);
273  catch(testuser->move(subroom));
274  Test.test("Permissions SUBROOM cached for user member of SUB group!",
275  get_permission(subroom, testuser) !=
276  (SANCTION_INSERT|SANCTION_READ));
277  seteuid(0);
278  subgroup->remove_member(testuser);
279  Test.test("Permissions acquired cleaned for user member of no group!",
280  get_permission(subroom, testuser) == 0,
281  "CACHE="+get_permission(subroom, testuser));
282  testgroup->add_member(subgroup);
283  subgroup->add_member(testuser);
284  object room2 = get_factory(CLASS_ROOM)->execute( (["name":"security_cache_sub_room2" ]));
285  room2->move(testgroup->get_workroom());
286  seteuid(testuser);
287  catch(testuser->move(room2));
288  Test.test("Permissions SUBROOM-2 cached for user member of group!",
289  get_permission(room2, testuser) !=
290  (SANCTION_INSERT|SANCTION_READ));
291  seteuid(0);
292  subgroup->remove_member(testuser);
293  Test.test("Permissions acquired cleaned for user member of no group (2)!",
294  get_permission(room2, testuser) == 0,
295  "CACHE="+get_permission(room2, testuser));
296  testgroup->get_workroom()->sanction_object(testuser, SANCTION_READ|SANCTION_INSERT);
297  object obj = get_factory(CLASS_OBJECT)->execute( (["name":"security_cache_obj" ]));
298  obj->sanction_object(testuser, SANCTION_MOVE);
299  seteuid(testuser);
300  obj->move(room2);
301  testuser->move(testgroup->get_workroom());
302  seteuid(0);
303  testgroup->get_workroom()->sanction_object(testuser, SANCTION_WRITE);
304  Test.test("Permissions acquired cleaned for user member of no group (3)!",
305  get_permission(room2, testuser) == 0,
306  "CACHE="+get_permission(room2, testuser));
307  Test.test("Direct Permissions cleaned for user!",
308  get_permission(testgroup->get_workroom(), testuser) == 0,
309  "CACHE="+get_permission(testgroup->get_workroom(), testuser));
310  obj->delete();
311  room2->delete();
312  subroom->delete();
313  subgroup->delete();
314  testuser->delete();
315  testgroup->delete();
316 
317  obj2->delete();
318 }
319 
320 string get_identifier() { return "Security:cache"; }
321 string get_table_name() { return "security_cache"; }
322 
323 
324 };