Jump to content

Archived

This topic is now archived and is closed to further replies.

Grayda

"Waterfall" effect?

Recommended Posts

I'm building in some new security modules for my web application and need some help regarding a simple waterfall or path-building solution

 

In my application, security is handled through a simple table like this:

 

Username

Access

Group

Edit

Grayda

somepage

AdminGroup

someotherpage

 

guest

[/td]

BasePages

 

BasePages

main,header,footer

AdminGroup

main,header,footer

[td]main,header,footer

 

 

In this example, Grayda can access somepage and edit someotherpage and is also in the group "AdminGroup". Guest is a part of the group "BasePages". If you follow the table down, you will see the AdminGroup can access "main", "header" and "footer" and can also edit "main", "header" and "footer". "BasePages" is the same as "AdminGroup" but without the edit permissions.

 

What I'm trying to build, is a function that will let me build a path from point A to B. For example if Grayda wants to access Main, and the path to Main is:

 

Grayda -> AdminGroup -> BaseGroup -> CoreGroup -> Main

 

then calling checkPermission("Grayda", "Main"); will return true because Grayda is in AdminGroup which points to BaseGroup etc. I've got simple one-level groups going on (eg. Grayda can access Main if it's only one level deep) but anything deeper eludes my knowledge.

 

Can anyone give me some assistance or some ideas on where to start with this?

 

Share this post


Link to post
Share on other sites

The easiest way I can think of is to create a system in which permissions are given by the 1s and 0s (bits) in an integer.

 

Take, for instance, these permissions: (binary added for clarity)

 

<?php
define('CREATE_POSTS', 1); // 0001
define('EDIT_POSTS', 2);   // 0010
define('DELETE_POSTS', 4); // 0100
define('BAN_MEMBERS', ;  // 1000
?>

 

Now that we have a few single action permissions, let's create permission groups. Say we want guests to have the ability to create posts, admins the ability to edit/delete posts (plus what guests can do), and super admins the ability to ban members (plus what admins can do)...

 

<?php
define('GUEST', CREATE_POSTS);                      // 0001 - Guests can create posts
define('ADMIN', EDIT_POSTS | DELETE_POSTS | GUEST); // 0111 - Admins can edit posts, delete posts, and do what guests do
define('SUPER_ADMIN', BAN_MEMBERS | ADMIN);         // 1111 - Super admins can ban members, and do what admins do
?>

 

Now we have a sample group of permissions, so let's try them out:

 

<?php
// Check whether the given user has the given permissions
function hasPermission($user, $permissions) {
   return ($user & $permissions) > 0;
}

// Set the user to be a guest
$user = GUEST;

// Test our function
hasPermission($user, CREATE_POSTS); // True - User can create posts
hasPermission($user, EDIT_POSTS); // False - User can not edit posts

// Set the user to be a super admin
$user = SUPER_ADMIN;

// Test our function
hasPermission($user, BAN_MEMBERS); // True - User can ban members now
hasPermission($user, EDIT_POSTS); // True - User can edit posts now
hasPermission($user, CREATE_POSTS); // True - User can also create posts
?>

 

Feel free to ask questions, and if you don't understand the bits with the | and & you may wish to look up how binary works and php's bitwise operators. Of course there are many other ways of doing this, but this is just the cleanest way for me to imagine it.

 

Edit: For a little clarity and formatting.

Share this post


Link to post
Share on other sites

Thanks for the information! I tried a sample implementation of your code and found that it was good for a fixed number of security levels and pages, but my application needed a little more dynamic-ness to handle custom pages and custom security levels. So I spent the last 3 hours poking around and came up with this which seems to work rather well so far:

 

<?php

// .. Some class code and variables here ..

// Function checkPermission
// $user = the username to check
// $uAccess = the section the user is trying to access
function checkPermission($user, $uAccess) {
		// This function fills up three variables: $this->access, $this->groups and $this->edit.
		// The data is pulled from the database and is split up using explode()
		$this->getPermissions($user);

		// First we loop through the groups we've got from the database
		for($i = 0; $i <= count($this->groups); $i++) {
			// I'm using ADOdb and some custom functions here.
			// This gets all the information related to the group in $this->groups[$i] (eg. AdminGroup or BasePages)
			$data = $this->database->getSQL("SELECT * FROM security WHERE Name = '" . $this->groups[$i] . "'");
			// Now, we explode the information. First the groups related to $this->groups[$i] then access information then edit information
			$group = explode(",", @$data[0]["Group"]);
			$access = explode(",", @$data[0]["Access"]);
			$edit = explode(",", @$data[0]["Edit"]);

			// Now comes the magic. Looping!				
			for($b = 0; $b <= count($group); $b++) {
				// If the groups aren't empty then:				
				if(!empty($group[0])) { 
					// Merge the results of $group and $this->groups
					$groupTemp = array_merge($this->groups, $group); 
					// Then return only the unique groups.
					// When the loop goes back around, it will re-calculate count($this->groups) and keep going until we stop adding stuff to the array
					$this->groups = array_unique($groupTemp); 
				} else {
					// But if the groups are empty, then we've come to the end of the line, so we 
					// merge all the information we've got so far into our class variables obtained above
					$accessTemp = array_merge($this->access, $access); 
					$this->access = array_unique($accessTemp); 
					$editTemp = array_merge($this->edit, $edit); 
					$this->edit = array_unique($accessTemp); 
				}
			}
			// Same goes for the access
			for($b = 0; $b <= count($access); $b++) {

				if(!empty($access[0])) { 
					$accessTemp = array_merge($this->access, $access); 
					$this->access = array_unique($accessTemp); 
				}
			}
			// And for edit
			for($b = 0; $b <= count($edit); $b++) {
				if(!empty($edit[0])) { 
					$editTemp = array_merge($this->edit, $edit); 
					$this->edit = array_unique($editTemp); 
				}
			}


		}
		// Finally, once we have all the access, group and edit information we need, we check to see if $uAccess is in the arrays and if it is, then we return true, meaning they have access. PHEW!
		if(in_array($uAccess, $this->access) or in_array($uAccess, $this->edit) or in_array($uAccess, $this->groups)) { return true; } else { return false; }
	}

?>

 

I tried it on some large group sets. (eg. guest -> blah1 -> blah2 ->blah3 ->blah4 -> blah5 -> main) and when I monitored the SQL calls through ADOdb, I got a series of calls to the right groups and at the end, it returned the data I was after. However, there might be some code optimizations I could make, and I should really test it with TONS of security objects.

 

But for anyone out there looking for a way to do this kind of thing, this might be for you?

Share this post


Link to post
Share on other sites

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.