1 /* Copyright (C) 2000-2004 Thomas Bopp, Thorsten Hampel, Ludger Merkens
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.
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.
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
17 * $Id: security_cache.pike,v 1.12 2010/01/26 15:45:39 astra Exp $
19 inherit "/kernel/secure_mapping";
21 #include <exception.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{
37 mapping mCache =([ ]);
38 int hits=0, total = 0;
39 private function myfDb;
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.");
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))");
56 FATAL("Failed to create security cache table: %O\n%O", err[0],err[1]);
60 mixed get_value(mixed idx) {
61 mixed res = mCache[idx];
65 res = ::get_value(idx);
70 mixed set_value(mixed idx, mixed val) {
72 ::set_value(idx, val);
75 string get_index(mixed obj, mixed user)
77 if (objectp(obj) && objectp(user)) {
78 return obj->get_object_id() + ":" + user->get_object_id();
80 return obj + ":" + user;
84 mixed set_permission(object obj, object user, int permission)
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) ) {
90 "insert into i_cache_security (obj, user, permissions) values ("+
91 obj->get_object_id() + "," + user->get_object_id() + "," +
95 myfDb()->big_query("update i_cache_security set permissions="+permission+" where " +
96 "user="+user->get_object_id() + " and obj="+obj->get_object_id());
98 string idx = get_index(obj, user);
99 mCache[idx] = permission;
105 void add_permission(object obj, object user, int value)
109 SECURITY_LOG("Cache: Adding permissions: %O, %O, %O\n", obj, user, value);
111 if ( !objectp(obj) ) return;
113 if ( user == _ROOT && value > ( 1<< SANCTION_SHIFT_DENY) )
114 THROW("Odd status of permissions - setting denied permissions for Root-user !", E_ERROR);
116 THROW("No permission to use security cache !", E_ACCESS);
118 // add all dependend objects into the databasese
119 object|function acquire = obj->get_acquire();
120 if ( functionp(acquire) ) acquire = acquire();
122 if ( objectp(acquire ) ) {
123 o_idx = acquire->get_object_id();
124 mixed val = get_value(o_idx);
127 if ( search(val, obj) == -1 ) {
128 set_value(o_idx, val + ({ obj }) );
132 perm = get_permission(obj, user);
133 set_permission(obj, user, perm | value);
136 void remove_permission(object obj)
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());
142 while (data = res->fetch_row()) {
143 string idx = get_index(data[0], data[1]);
144 m_delete(mCache, idx);
148 depends = get_value(obj->get_object_id());
149 if ( arrayp(depends) )
150 foreach(depends, object dep)
151 remove_permission(dep);
153 myfDb()->big_query("delete from i_cache_security where obj="+ obj->get_object_id());
154 m_delete(mCache, obj->get_object_id());
157 void remove_permission_user(object user)
159 Sql.sql_result res = myfDb()->big_query("select obj, user from i_cache_security where user="+user->get_object_id());
161 while (data = res->fetch_row()) {
162 string idx = get_index(data[0], data[1]);
163 m_delete(mCache, idx);
165 myfDb()->big_query("delete from i_cache_security where user=" + user->get_object_id());
168 int get_permission(object obj, object user)
170 if ( !objectp(obj) || !objectp(user) ) return 0;
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();
175 int permission = (int)row[0];
176 mCache[get_index(obj, user)] = permission;
184 if ( GROUP("admin")->is_member(this_user()) ) {
189 steam_error("Unauthorized call to clear_cache()");
192 object testuser = get_factory(CLASS_USER)->execute( (["name": "security_cache_test_user", "pw":"test", "email": "xyz",]) );
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);
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);
205 object testgroup = get_factory(CLASS_GROUP)->execute( (["name":"security_cache_test_group" ]));
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));
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);
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) ==
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);
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));
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);
260 catch(testuser->move(testgroup->get_workroom()));
262 Test.test("Permissions cached for user member of SUB group!",
263 get_permission(testgroup->get_workroom(), testuser) !=
264 (SANCTION_INSERT|SANCTION_READ));
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());
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));
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());
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));
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);
301 testuser->move(testgroup->get_workroom());
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));
320 string get_identifier() { return "Security:cache"; }
321 string get_table_name() { return "security_cache"; }