Roles-Based Security

From Achievo/ATK Wiki

Jump to: navigation, search

ATK Howto: Roles-Based Security

Complexity: Intermediate
Author: --Rwolverine 14:18, 7 December 2007 (CET) / Notes added 2011 by wayneh

List of other Howto's

How to Define Role-Based Security using the ATK.

This was handed around via email upon request for awhile and appears to be origonally authored by Elad Messing, He, Ivo Jansch, and I'm assuming the rest of the ATK development team collected this all into one document.. I pass all the credit onto them, and only take credit for posting it here on the wiki :).

Contents

Introduction

This tutorial explains first how to setup the admin part for the security (how to create a user, how to define what he/she can do...). Once we have created this access right framework, we are going to configure ATK so it actually uses these access rights. On ATK, each module allows several actions, by default, these actions are “admin”, “add”, “delete”, “edit”. Creating new actions is outside the scope of this how to (but see the howto on Custom (record) actions)?

The security mechanism is based on “Entities”

An entity is a group of users. It can be a department, a group, a profile, a role, depending on how you want to name them. On this document, we are going to call them “profile”, but the name doesn't matter much, as long as you understand the principle.

For each profile, the administrator is going to grant different “permissions” on all or some actions to each module of the application. Each profile might have. Normal users might be able to only “admin” a node, while power users will also be able to “add”, “edit” and “delete” records in the node. This isolates security privileges from individuals thereby simplifying and streamlining security management.

Configuration options

There are 2 common options for managing users and profiles. Either a user is part of one profile, or a user is part of many (one or more) profiles. In this tutorial we will explain both methods.

User is part of one profile

Please note the atk demo application has a lesson (Lesson 5)that deal with this configuration exactly. In case you have any difficulties – please refer to the code, comments and tables in the demo application.

First, we are going to install the framework (the users, profiles...) needed to manage this security scheme and see how to configure atk to manage these items (after all, that's going to be content stored in table, and therefore is going to be managed by atk as nodes and modules).

Node Structure

Our new module is called “users”. So we add a new sub-directory in the modules directory, and add the module.inc file first. Don't forget to load this new module in the config.modules.inc! We need two nodes: a user node and a profile node.

************************** users/module.inc **************************************
 
class mod_users extends atkModule
{
  function getNodes()
  {
    /* register nodes */
    registerNode("users.user", array("admin", "add", "edit", "delete"));
    registerNode("users.profile", array("admin", "add", "edit", "delete"));
  }
 
  function getMenuItems()
  {
    /**
     * The next line adds a submenu called 'users' to the main menu.
     * Be careful not to use the same text for the mainmenu item and the submenu item.
     */
    menuitem("user_admin");
    menuitem("users", dispatch_url("users.user", "admin"),"user_admin");
    menuitem("groups", dispatch_url("users.profile", "admin"),"user_admin");
  }
}
************************** users/module.inc **************************************



Users

The user has a login and password and belongs to a profile.

First you need to create the following table:

CREATE TABLE `user` (
`user_id` int(11) NOT NULL DEFAULT '0',
`login` varchar(25) NOT NULL DEFAULT '',
`name` varchar(50) NOT NULL DEFAULT '',
`password` varchar(50) DEFAULT NULL,
`profile_id` int(11) DEFAULT NULL,
PRIMARY KEY (`user_id`),
KEY `login` (`login`)
)

Note that we can off-course add more fields like “email” but currently they are irrelevant.

Then we add the user node:

************************** users/class.user.php ************************************** 
useattrib("atkemailattribute");
useattrib("atkpasswordattribute");
userelation("atkonetomanyrelation");
userelation("atkmanytoonerelation");
 
class User extends atkNode
{
function User()
{
$this->atkNode("user", NF_ADD_LINK);
 
$this->add(new atkAttribute("user_id", AF_AUTOKEY));
$this->add(new atkAttribute("name", AF_OBLIGATORY|AF_SEARCHABLE));
 
$this->add(new atkAttribute("login", AF_OBLIGATORY|AF_UNIQUE|
AF_SEARCHABLE));
 
$this->add(new atkPasswordAttribute("password",true,AF_HIDE_LIST|
AF_PASSWORD_NOVALIDATE));
 
//link to profiles
$this->add(new atkManyToOneRelation("profile_id", "users.profile", AF_RELATION_AUTOLINK|AF_HIDE_ADD));
 
$this->setOrder("name");
$this->setTable("user");
}
 
function descriptor_def()
{
return "[name]";
}
}
************************** users/class.user.inc **************************************

As you can see, there is a link between each user and a profile.



Profile

Here too, you have to create DB tables (this time two tables, one for the profile, the other to store the access rights).

CREATE TABLE `profile` (
`profile_id` int(11) NOT NULL DEFAULT '0',
`name` varchar(100) NOT NULL DEFAULT '',
PRIMARY KEY (`profile_id`)
) 
 
CREATE TABLE `access` (
`node` varchar(100) NOT NULL DEFAULT '',
`action` varchar(25) NOT NULL DEFAULT '',
`profile_id` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`node`,`action`,`profile_id`)
)

Note that the "node" and "action" columns in the "access" table must be exactly as shown (including case).

If you change them in any way (such as capitalizing the column names), your profile permissions will be saved, but not loaded again later (resulting in users not having permissions to anything and all of the checkboxes for profile permissions being empty when you view the profile again later).

And add the profile node:

************************** users/class.profile.inc ************************************** 
 
 
useattrib("atkdummyattribute");
useattrib("atkprofileattribute");
 
class profile extends atkNode
{
function profile()
{
$this->atkNode("profile", NF_EDITAFTERADD|NF_NO_VIEW);
 
$this->add(new atkAttribute("profile_id",AF_AUTOKEY));
 
$this->add(new atkAttribute("name",AF_OBLIGATORY|AF_UNIQUE|
AF_SEARCHABLE, 50));
 
$this->add(new atkDummyAttribute("profile_explanation",text
("profile_explanation"),AF_HIDE_LIST|AF_HIDE_ADD));
 
$this->add(new atkProfileAttribute("accessrights",AF_HIDE_LIST|
AF_HIDE_ADD|AF_BLANKLABEL));
 
$this->setTable("profile");
 
$this->setOrder("name");
 
}
function descriptor_def()
{
return "[name]";
}
}
 
************************** users/class.profile.inc **************************************

Here, you see a new type of attribute “atkProfileAttribute” that does all the magic. This attribute is going to list all the modules and each action available and allow you to grant them to the profile (so the users belonging to that profile can do the action).

Configuration of atk

We are almost done and the only missing step is to tell atk to use the access rights defined in these tables. For that, you need to modify the file config.inc.php : 1) you need to change the config_authentication and set it to “db”

// $config_authentication = "none";
$config_authentication = "db";
 
2) You need to tell atk what the tables that store the access rights are:
 
$config_auth_usertable = "user";
$config_auth_userfield = "login";
$config_auth_passwordfield = "password";
$config_auth_leveltable = "user";
$config_auth_levelfield = "profile_id";
$config_auth_accesstable = "access";

That’s it! Now you can login to the application and review the screens that atk generates for you. All the functionality is already there for you to use.



User can be part of one or more profiles

This method is a bit more advanced, and allows you to configure different profiles, when each profile has different set of permissions. For example – the “normal” profile has the “admin” and “edit” permissions, and the “power” profile has the “delete” permission. A user with both “normal” and “power” profiles will be able to “admin”, “edit” and “delete”.

ATK can support this configuration as well, but some changes (to the above installation) needs to be made.

Node Structure

Just like when users are part of only one profile we need to have the same modules as above:


************************** users/module.inc **************************************
 
class mod_users extends atkModule
{
function getNodes()
{
/* register nodes */
registerNode("users.user", array("admin", "add", "edit", "delete"));
registerNode("users.profile", array("admin", "add", "edit", "delete"));
}
 
function getMenuItems()
{
/**
* The next line adds a submenu called 'users' to the main menu.
*/
menuitem("users_admin");
menuitem("users", dispatch_url("users.user", "admin"),"users_admin");
menuitem("groups", dispatch_url("users.profile", "admin"),"users_admin");
}
}
************************** users/module.inc **************************************


Users

In the user DB table, the “profile_id” filed is no longer needed. This is because we will now have a separate table managing the connection between the users and the profiles.

Further more – the “profile_id” relation attribute in the “users” node is no longer needed, and is replaced by a special relation “atkManyBoolRelation” :

************************** users/class.user.inc **************************************
 
//$this->add(new atkManyToOneRelation("profile_id", "users.profile", AF_RELATION_AUTOLINK|AF_HIDE_ADD));
$rel = &$this->add(new atkManyBoolRelation("profiles", "users.userprofile", "users.profile", AF_HIDE_LIST|AF_MANYBOOL_AUTOLINK));
 
$rel->setLocalKey("user_id");
 
$rel->setRemoteKey("profile_id");
 
************************** users/class.user.inc **************************************

This will allow us to edit the user, and assign the user to one or more profiles.

User-Profile

In order to established the relationship between 1 user and many profiles, we need a supporting table.

CREATE TABLE `userprofile` (
`user_id` int(11) NOT NULL,
`profile_id` int(11) NOT NULL,
PRIMARY KEY (`user_id`,`profile_id`)
)

As you can see, this is a simple “many-to-many” support table.

Here is the “userprofile” node:

************************** users/class.userprofile.inc ************************************** 
 
 
userelation("atkmanytoonerelation");
class userprofile extends atkNode
{
	function userprofile ()
	{
		$this->atkNode("userprofile", NF_NO_EDIT);
 
		$this->add (new atkManyToOneRelation("user_id", "users.user", AF_PRIMARY));
		$this->add (new atkManyToOneRelation("profile_id", "users.profile", AF_PRIMARY));
		$this->setTable("userprofile");
	}
 
    /**
     * Return record descriptor (for use in relations etc.)
     * @return String A template containing fieldnames between brackets.
     */
	function descriptor_def()
	{
		return "[user_id] , [profile_id]";
	}
}
 
************************** users/class.userprofile.inc **************************************

This node reflects the userprofile table structure, with 2 mant-to-one relationships – to users and to profiles. Theoretically – (just to emphasis the meaning of the node) you can ask this node for all profiles of the user_id=“1”, or all users that are part of the profile_id=”2”.

Configuration of atk

We already told atk (in the previous section”) what is our users / profiles tables. We now need to update the configuration, and tell atk that there is a now table to use :

$config_auth_usertable = "user";
$config_auth_userfield = "login";
$config_auth_passwordfield = "password";
$config_auth_userpk      = "user_id";
 
$config_auth_leveltable = "userprofile";
$config_auth_levelfield = "profile_id";
$config_auth_userfk = "user_id";
$config_auth_accesstable = "access";

With these 3 new lines we are telling atk to look for the profile information for each user in the “userprofile” table, while also mentioning the names of the fields in this table.

Notes for Oracle users

Oracle users should name the "user" table "users" "access" table "accessrights" (or make similar changes) to avoid reserved/key words. Also, when creating the tables, do NOT put double-quotes around table/field names, instead allow Oracle to uppercase the names automatically. (ATK's database calls do not double-quote field and table names so you'll get a bunch of "table not found" and "field not found" type errors if you use the double-quotes to force Oracle to keep the table/field names in lowercase.)

Conclusion

That’s it! Now you can login to the application and review the screens that atk generates for you. First add 2 new profiles. Than add a new user. After that edit the user, and you can see that a new control is added, allowing you to assign 1 or both profiles for the user. The DB tables are updated accordingly.

NOTE re Password Content Restrictions

Replace -

$this->add(new atkPasswordAttribute("password",true,AF_HIDE_LIST|
AF_PASSWORD_NOVALIDATE));

with -

$this->add(new atkPasswordAttribute("password", false, AF_HIDE_LIST, 0, atkconfig("password_restrictions", array())));

and add -

// 0 => ignore restriction
// >0 => implement restriction
// Special characters are    !@#$%^&*()-+_=[]{}\|;:'\",.<>/?
 
$config_password_restrictions = array(
    "minsize" => 6,
    "minupperchars" => 1,
    "minlowerchars" => 0,
    "minalphabeticchars" => 0,
    "minnumbers" => 0,
    "minspecialchars" => 0
    );

to config.inc.php with the values you require


NOTE re Controlling Access to Tabs

By registering Tab names in registerNode() atkProfileAttribute knows about them and access to the tabs can be turned on/off from the same screen as turning access on/off to admin/add/edit/delete for the node

eg

registerNode("customer.customer",array("admin","add","edit","delete"),array("Details","Addresses","PhoneNos"))

where Details, Addresses and PhoneNos are tab names

Then use statements like

if($this-allowed("tab_tab_name")
{
      .. what to do if allowed - eg add attributes for this tab   
}

Note that the "allowed" parameter is tab_ followed by the tab name and is case sensitive so could be "tab_Details", "tab_Addresses" or "tab_PhoneNos" to match the registerNode entry above

Sourced from Achievo dev forum http://forum.achievo.org/viewtopic.php?f=5&t=16251&start=0&hilit=register+tab

NOTE re BUG checktabrights (in atknode)

This function has a bug which stops it working so only the $this->allowed functionality above works However if you patch checktabrights (see BUGZILLA http://bugzilla.achievo.org/show_bug.cgi?id=1708 ) all is OK

NOTE reauth_grantall_privilege

If a user has the grantall privilege, he can grant other users all privileges; even privileges he does not have himself. Without this privilege, users can only grant rights to other users that they have themselves.

Set in config file with
$config_auth_grantall_privilege = "grantall";
Register this action with
registerNode("????????.profile", array("admin", "add", "edit", "delete", "grantall"));
in you profile node

Then tick the "grantall" box in your profile node add/edit screen


Wayne H Nov 2009 / updated May 2011 / updated July 2011

Personal tools
Navigation