SitePoint Sponsor

User Tag List

Page 3 of 11 FirstFirst 1234567 ... LastLast
Results 51 to 75 of 267
  1. #51
    Non-Member
    Join Date
    Jan 2004
    Location
    Planet Earth
    Posts
    1,764
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Thanks Marcus, as always you have something usefull to contribute

    Nothing wrong with this part though, looks clean enough IMO.

    PHP Code:
    ...
    $authoriser = &new Authoriser(); 
            
    $authoriser->addUsage('fred'); 
            
    $authoriser->addRole('pleb'); 
            
    $authoriser->addOperation('do_stuff'); 
            
    $authoriser->attachRole('fred''pleb'); 
            
    $authoriser->permit('pleb''do_stuff'); 
    ... 
    Makes sense really

    If I can help, I would like too.
    The more, the merrier

  2. #52
    SitePoint Evangelist
    Join Date
    Dec 2003
    Location
    Arizona
    Posts
    411
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    It's looking good, Marcus. Here are my comments:

    1. Why did you decide to separate out the AccessController from the Authorizer?

    2. Naming - what is the difference between an operation and a permission? I think the naming should be consistent between the Authorizer class and the Permissions class. So if we go with just "permission", I think instead of addOperation/dropOperation, I would go with grantPermission, revokePermission. This is just a matter of preference, I guess.

    Thanks,

    JT

  3. #53
    ********* Victim lastcraft's Avatar
    Join Date
    Apr 2003
    Location
    London
    Posts
    2,423
    Mentioned
    2 Post(s)
    Tagged
    0 Thread(s)
    Hi...

    Quote Originally Posted by seratonin
    1. Why did you decide to separate out the AccessController from the Authorizer?
    This is possibly a premature optimisation. As the most common operation is checking a permissions set, rather than modifying the permissions map, it seemed to make sense to keep this part of the code lean and mean. Otherwise you could end up with a lot of unecessary require_once()'s for the most common operation. I may not have chosen the best approach though. It would be nice to have just one class.

    Quote Originally Posted by seratonin
    I think the naming should be consistent between the Authorizer class and the Permissions class.
    Good point. Permissions is a releationship and "permit" is a transient act, hence why I chose a different name. Permissions is obviously correct in the access control interface and it does make it more consistent to do it the same way in the admin. interface. Please repost your modifications.

    I see I have chosen English (UK) spellings without thinking as well. It's the colonial past catching up with me .

    yours, Marcus

    p.s. Seratonin: As you have access to the first post in the script, do you fancy updating it as a summary of what is happening as we go?
    Marcus Baker
    Testing: SimpleTest, Cgreen, Fakemail
    Other: Phemto dependency injector
    Books: PHP in Action, 97 things

  4. #54
    SitePoint Evangelist
    Join Date
    Dec 2003
    Location
    Arizona
    Posts
    411
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by lastcraft
    This is possibly a premature optimisation. As the most common operation is checking a permissions set, rather than modifying the permissions map, it seemed to make sense to keep this part of the code lean and mean. Otherwise you could end up with a lot of unecessary require_once()'s for the most common operation. I may not have chosen the best approach though. It would be nice to have just one class.
    That makes sense. Since updating permissions happens much less frequently than retreiving permissions, the separation is a good idea.

    Quote Originally Posted by lastcraft
    Good point. Permissions is a releationship and "permit" is a transient act, hence why I chose a different name. Permissions is obviously correct in the access control interface and it does make it more consistent to do it the same way in the admin. interface. Please repost your modifications.
    I think that Authorizer is a deceiving name anyways for the Admin interface. Something like PermissionsAdmin, RbacAdmin, or AccessControlAdmin might be a better name since it's sole reponsiblity is the administrative functionality. Maybe we have two classes AccessController (responsible for controlling access) and AccessAdministrator (responsible for administrating access).

    Quote Originally Posted by lastcraft
    p.s. Seratonin: As you have access to the first post in the script, do you fancy updating it as a summary of what is happening as we go?
    I will update the first post with a summary of the topic. We may want to start a (part 2) to this forum so that it doesn't become too cluttered.

    JT
    Last edited by seratonin; Apr 9, 2004 at 13:57.

  5. #55
    ********* Victim lastcraft's Avatar
    Join Date
    Apr 2003
    Location
    London
    Posts
    2,423
    Mentioned
    2 Post(s)
    Tagged
    0 Thread(s)
    Hi...

    Quote Originally Posted by seratonin
    Maybe we have two classes AccessController (responsible for controlling access) and AccessAdministrator (responsible for administrating access).
    Or Authoriser and AuthorisationEditor respectively. In fact I think these are the "right" names. I think these names are more precise than names with controller and manager in. They always make me uneasy.

    Quote Originally Posted by seratonin
    I will update the first post with a summary of the topic. We may want to start a (part 2) to this forum so that it doesn't become too cluttered.
    Is this a good idea? Having to jump from post to post is going to cause chaos, especially if someone posts something in a previous part causing a fork. I haven't had a problem with the long post yet, and I think it is good to tell the whole story. There may be a bigger problem with digressions though.

    On testing: I have posted the acceptance test because I think it is a good way of nailing down the spec. I'll avoid posting every unit test if I can though, as that will double LOC.

    The next bit should be the actual Authoriser::getPermissions() method and making the editor transactional. These are the next big issues I think. Anyway - so far we are here, yes...?
    PHP Code:
    class RoleBasedPermissionsTest extends UnitTestCase 
        function 
    RoleBasedPermissionsTest() { 
            
    $this->UnitTestCase(); 
        } 
        function 
    setUp() { 
            
    $editor = &new AuthorisionEditor(); 
            
    $editor->addUsage('fred'); 
            
    $editor->addRole('pleb'); 
            
    $editor->addPermission('do_stuff'); 
            
    $editor->attachRole('fred''pleb'); 
            
    $editor->permit('pleb''do_stuff');
            
    $editor->commit();
        } 
        function 
    tearDown() { 
            
    $editor = &new AuthorisionEditor(); 
            
    $editor->dropUsage('fred'); 
            
    $editor->dropRole('pleb'); 
            
    $editor->dropPermission('do_stuff'); 
            
    $editor->commit();
        } 
        function 
    testNonUserHasNothingAllowed() {
            
    $authoriser = &new Authoriser();
            
    $permissions = &$authoriser->getPermissions('public'); 
            
    $this->assertFalse($permissions->can('do_stuff')); 
        } 
        function 
    testBadPasswordHasNothingAllowed() { 
            
    $authoriser = &new Authoriser();
            
    $permissions = &$authoriser->getPermissions('fred'); 
            
    $this->assertFalse($permissions->can('do_stuff')); 
        } 
        function 
    testLegitimateUserHasActionAllowed() { 
            
    $authoriser = &new Authoriser();
            
    $permissions = &$authoriser->getPermissions('fred'); 
            
    $this->assertTrue($permissions->can('do_stuff')); 
        } 
        function 
    testUserCannotDoNonAction() { 
            
    $authoriser = &new Authoriser();
            
    $permissions = &$authoriser->getPermissions('fred'); 
            
    $this->assertFalse($permissions->can('do_unknown')); 
        } 

    yours, Marcus
    Marcus Baker
    Testing: SimpleTest, Cgreen, Fakemail
    Other: Phemto dependency injector
    Books: PHP in Action, 97 things

  6. #56
    SitePoint Evangelist
    Join Date
    Dec 2003
    Location
    Arizona
    Posts
    411
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by lastcraft
    Is this a good idea? Having to jump from post to post is going to cause chaos, especially if someone posts something in a previous part causing a fork. I haven't had a problem with the long post yet, and I think it is good to tell the whole story. There may be a bigger problem with digressions though.
    I will update the first message in this topic and we will continue as we are.

    Thanks,

    JT

  7. #57
    ********* Victim lastcraft's Avatar
    Join Date
    Apr 2003
    Location
    London
    Posts
    2,423
    Mentioned
    2 Post(s)
    Tagged
    0 Thread(s)
    Hi...

    Just to get some grunt work out of teh way, here is options.php. Should be all the Singleton/global data we ever need.
    PHP Code:
    <?php
    class AuthoriserOptions {
        
        function 
    setConfigurationFile($file) {
            
    $registry = &AuthoriserOptions::_getRegistry();
            
    $registry['file'] = $file;
        }
        
        function 
    getConfigurationFile() {
            
    $registry = &AuthoriserOptions::_getRegistry();
            return 
    $registry['file'];
        }
        
        function 
    setConfigurationClass($class) {
            
    $registry = &AuthoriserOptions::_getRegistry();
            
    $registry['class'] = $class;
        }
        
        function 
    getConfigurationClass() {
            
    $registry = &AuthoriserOptions::_getRegistry();
            return 
    $registry['class'];
        }
        
        function &
    _getRegistry() {
            static 
    $registry false;
            if (! 
    $registry) {
                
    $registry = array(
                        
    'file' => false,
                        
    'class' => 'AuthoriserConfiguration');
            }
            return 
    $registry;
        }
    }
    ?>
    yours, Marcus
    Marcus Baker
    Testing: SimpleTest, Cgreen, Fakemail
    Other: Phemto dependency injector
    Books: PHP in Action, 97 things

  8. #58
    Non-Member
    Join Date
    Jan 2004
    Location
    Planet Earth
    Posts
    1,764
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Now I'm lost as to why you need this class ??

  9. #59
    ********* Victim lastcraft's Avatar
    Join Date
    Apr 2003
    Location
    London
    Posts
    2,423
    Mentioned
    2 Post(s)
    Tagged
    0 Thread(s)
    Hi...

    Quote Originally Posted by Widow Maker
    Now I'm lost as to why you need this class ??
    Excellent question . I have gone completely mad. Basically I was trying to get this to work...
    PHP Code:
    <?php
    class AuthorisationEditor {
        var 
    $_storage;
        
        function 
    AuthorisationEditor() {
            
    $configuration_class =
                    
    AuthorisationOptions::getConfigurationClass();
            
    $configuration = &new $configuration_class(
                    
    AuthorisationOptions::getConfigurationFile());
            
    $this->_storage = &$this->_createStorage($configuration);
        }
        function &
    _createStorage($configuration) {
            
    $storage_class $configuration->get('storage_class');
            return new 
    $storage_class();
        }
        function 
    addUsage() {
        }
        ...
    }
    ?>
    There are three hooks in this. The aim is to get the appropriate storage factory set up. This needs a configuration file if it's shipped as part of a package. In order to run the acceptance tests and to make things configurable, the name of this file needs to be changed. That is number one. If the library is extended then the configuration class would have to extended too and that's two. The last hook is the protected factory method for swapping the storage for testing. All to avoid passing stuff in the constructor.

    I now think it is way too complicated and a generally bad idea .

    Assume that we need a storage driver class for a moment (even this is probably wrong). If we allow constructor parameters for the AuthorisationEditor() then a configuration file is not needed at all. The catch is that the AuthorisationEditor and the Authoriser must get to use the same storage mechanism. If we place this responsibility on the application then changes to one may not get rippled through. There are several much better solutions to this.

    1) Pass in the configuration object/hash. The application probably already has one anyway.
    2) Have the application create the storage class anyway.
    3) Have the editor created from a factory in the Authoriser.

    No need for options stuff at all either way .

    Trouble is I am now having problems with the design above . It is really hard to add code to the editor that is not SQL dependent without moving everything into the storage class (which just moves the problem). To be flexible I think the solution is to have different editors (which are basically data accessors in this data centric problem).

    Abandoning the class separation (which now seems to cause way to many complications)...
    PHP Code:
    <?php
    require_once('../authoriser.php');
    require_once(
    '../configuration.php');

    class 
    RoleBasedPermissionsTest extends UnitTestCase 
        function 
    RoleBasedPermissionsTest() { 
            
    $this->UnitTestCase();
            
    $this->_configuration = &new AuthorisationConfiguration();
        } 
        function 
    setUp() { 
            
    $authoriser = &new Authoriser($this->_configuration);
            
    $edit = &$authorisor->createEdit(); 
            
    $edit->addUsage('fred'); 
            
    $edit->addRole('pleb'); 
            
    $edit->addPermission('do_stuff'); 
            
    $edit->assign('fred''pleb'); 
            
    $edit->permit('pleb''do_stuff');
            
    $edit->commit();
        } 
        function 
    tearDown() { 
            
    $authoriser = &new Authoriser($this->_configuration);
            
    $edit = &$authorisor->createEdit(); 
            
    $edit->dropUsage('fred'); 
            
    $edit->dropRole('pleb'); 
            
    $edit->dropPermission('do_stuff'); 
            
    $edit->commit();
        } 
        function 
    testNonUserHasNothingAllowed() {
            
    $authoriser = &new Authoriser($this->_configuration);
            
    $permissions = &$authoriser->getPermissions('public');
            
    $this->assertFalse($permissions->can('do_stuff')); 
        } 
        ...

    ?>
    This is much simpler. Now the storage is created within the Authoriser only (from a class name) and in turn it has to create the PermissionsFinder and AuthorisationAccessor (basically the edit).

    Still thrashing, but I feel I am getting somewhere. What have I done wrong now ?

    yours, Marcus
    Marcus Baker
    Testing: SimpleTest, Cgreen, Fakemail
    Other: Phemto dependency injector
    Books: PHP in Action, 97 things

  10. #60
    Non-Member
    Join Date
    Jan 2004
    Location
    Planet Earth
    Posts
    1,764
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    We may want to start a (part 2) to this forum so that it doesn't become too cluttered.
    If you can think of a catchy title to go with it, then even better I think this thread is already cluttered myself, so another thread would be great, just to point out as to where the project is at the moment ?

    If someone could then post what script has been done thus far ?

  11. #61
    ********* Victim lastcraft's Avatar
    Join Date
    Apr 2003
    Location
    London
    Posts
    2,423
    Mentioned
    2 Post(s)
    Tagged
    0 Thread(s)
    Hi...

    Still thinking aloud, what if the usage were like this...?
    PHP Code:
    $authoriser = &new Authoriser(new MySqlStorage(), New RbacMechanism()); 
    That is the choice of administration could be configured within the app. as well. I don't tink this should be added yet (I don't see much commonality and its scope creep again).

    Also, after a discussion with Meryn in another thread, I think too much construction is happening if a configuration file is passed. Clients of the library can subclass for this behaviour.

    Another go just to establish a reference skeleton...
    PHP Code:
    <?php
    require_once('../authoriser.php');
    require_once(
    '../pear_storage.php');

    class 
    RoleBasedPermissionsTest extends UnitTestCase 
        function 
    RoleBasedPermissionsTest() { 
            
    $this->UnitTestCase();
            
    $this->storage = &new PearAuthorisationStorage(CONNECTION_STRING);
        } 
        function 
    setUp() { 
            
    $authoriser = &new Authoriser($this->storage);
            
    $edit = &$authoriser->createEdit(); 
            
    $edit->addUsage('fred'); 
            
    $edit->addRole('pleb'); 
            
    $edit->addPermission('do_stuff'); 
            
    $edit->assign('fred''pleb'); 
            
    $edit->permit('pleb''do_stuff');
            
    $edit->commit();
        } 
        function 
    tearDown() { 
            
    $authoriser = &new Authoriser($this->storage);
            
    $edit = &$authoriser->createEdit(); 
            
    $edit->dropUsage('fred'); 
            
    $edit->dropRole('pleb'); 
            
    $edit->dropPermission('do_stuff'); 
            
    $edit->commit();
        } 
        function 
    testNonUserHasNothingAllowed() {
            
    $authoriser = &new Authoriser($this->storage);
            
    $permissions = &$authoriser->getPermissions('public');
            
    $this->assertFalse($permissions->can('do_stuff')); 
        } 
        function 
    testBadPasswordHasNothingAllowed() { 
            
    $authoriser = &new Authoriser($this->storage);
            
    $permissions = &$authoriser->getPermissions('fred'); 
            
    $this->assertFalse($permissions->can('do_stuff')); 
        } 
        function 
    testLegitimateUserHasActionAllowed() { 
            
    $authoriser = &new Authoriser($this->storage);
            
    $permissions = &$authoriser->getPermissions('fred'); 
            
    $this->assertTrue($permissions->can('do_stuff')); 
        } 
        function 
    testUserCannotDoNonAction() { 
            
    $authoriser = &new Authoriser($this->storage);
            
    $permissions = &$authoriser->getPermissions('fred'); 
            
    $this->assertFalse($permissions->can('do_unknown')); 
        } 

    ?>
    I'll try to get these tests to run again (and probably embarrass myself again) in the next post.

    yours, Marcus
    Last edited by lastcraft; Apr 12, 2004 at 11:27.
    Marcus Baker
    Testing: SimpleTest, Cgreen, Fakemail
    Other: Phemto dependency injector
    Books: PHP in Action, 97 things

  12. #62
    ********* Victim lastcraft's Avatar
    Join Date
    Apr 2003
    Location
    London
    Posts
    2,423
    Mentioned
    2 Post(s)
    Tagged
    0 Thread(s)
    Hi...

    Here is enough to get some structure into teh design. Hack to taste.

    Here is a top level authoriser.php...
    PHP Code:
    <?php
    require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR 'permissions.php');
    require_once(
    dirname(__FILE__) . DIRECTORY_SEPARATOR 'cache.php');

    class 
    Authoriser {
        var 
    $_storage;
        
        function 
    Authoriser(&$storage) {
            
    $this->_storage = &$storage;
        }
        function &
    getPermissions($name) {
            
    $finder = &new PermissionsCache($this->_storage->createPermissionsFinder());
            return new 
    Permissions($finder->findAllByName($name));
        }
        function &
    createEdit() {
            return 
    $this->_storage->createEdit();
        }
    }
    ?>
    The cache in cache.php does nothing...
    PHP Code:
    <?php
    class PermissionsCache {
        var 
    $_finder;
        
        function 
    PermissionsCache(&$finder) {
            
    $this->_finder = &$finder;
        }
        function 
    findAllByName($name) {
            return 
    $this->_finder->findAllByName($name);
        }
    }
    ?>
    The permissions class in permissions.php has a long way to go...
    PHP Code:
    <?php
    class Permissions {
        function 
    Permissions($all) {
        }
        function 
    can() {
        }
    }
    ?>
    I guess it just takes a list in the constructor.

    All of the real work is in the sample storage classes, here pear_storage.php as an example...
    PHP Code:
    <?php
    class PearAuthorisationStorage {
        var 
    $_connection;
        
        function 
    PearAuthorisationStorage($connection_string) {
            
    $this->_connection = &$this->_createConnection($connection_string);
        }
        function 
    createPermissionsFinder() {
            return new 
    PearPermissionsFinder($this->_connection);
        }
        function &
    createEdit() {
            return new 
    PearAuthorisationEdit($this->_connection);
        }
        function &
    _createConnection($connection_string) {
        }
    }

    class 
    PearPermissionsFinder {
        function 
    PearPermissionsFinder(&$connection) {
        }
        function 
    findAllByName($name) {
        }
    }

    class 
    PearAuthorisationEdit {
        
        function 
    PearAuthorisationEdit(&$connection) {
        }
        function 
    addUsage($name) {
        }
        function 
    dropUsage($name) {
        }
        function 
    addRole($role) {
        }
        function 
    dropRole($role) {
        }
        function 
    addPermission($action) {
        }
        function 
    dropPermission($action) {
        }
        function 
    assign($name$role) {
        }
        function 
    permit($role$action) {
        }
        function 
    commit() {
        }
    }
    ?>
    It should be possible to flesh these out. The only tricky one is the cache as this needs to be configurable just like the deep storage. This means changing the interface again .

    Any takers? I wouldn't want to end up writing the whole thing, but I will if no one manages to stop me .

    yours, Marcus
    Marcus Baker
    Testing: SimpleTest, Cgreen, Fakemail
    Other: Phemto dependency injector
    Books: PHP in Action, 97 things

  13. #63
    SitePoint Evangelist
    Join Date
    Dec 2003
    Location
    Arizona
    Posts
    411
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by lastcraft
    It should be possible to flesh these out. The only tricky one is the cache as this needs to be configurable just like the deep storage. This means changing the interface again .

    Any takers? I wouldn't want to end up writing the whole thing, but I will if no one manages to stop me .

    yours, Marcus
    Thanks, Marcus. I will look over your code and see what "blanks" I can "fill-in".

    JT

  14. #64
    SitePoint Evangelist
    Join Date
    Dec 2003
    Location
    Arizona
    Posts
    411
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    It seems that since we are not really using a DomainModel here that a the TableGateway pattern will work better than a DataMapper (especially in the PearAuthorisationEdit class). I am under the impression that a DataMapper is useful only when you are returning a Domain Object (or a collection of the Domain Objects) to the client. The PermissionsFinder can still work (as the Permissions object *is* a Domain Object), I'm mainly talking about the AuthorisationEdit class.

    JT
    Last edited by seratonin; Apr 12, 2004 at 21:15.

  15. #65
    SitePoint Evangelist
    Join Date
    Dec 2003
    Location
    Arizona
    Posts
    411
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Here is my "stab" at Permissions, PearAuthorisationStorage, and PearPermissionFinder:

    PHP Code:
    <?php
    class Permissions {
        var 
    $_all;

        function 
    Permissions(&$all) {
            
    $this->_all &= $all;
        }

        function 
    can($action) {
            return 
    in_array($action$this->_all);
        }
    }
    ?>
    PHP Code:
    <?php
    require_once("DB.php");

    // Same format as HarryF's PHP Anthology
    define('USAGE_TABLE''usage');
    define('USAGE_TABLE_ID''id');
    define('USAGE_TABLE_NAME''name');
    define('ROLE_TABLE''role');
    define('ROLE_TABLE_ID''id');
    define('ROLE_TABLE_NAME''name');
    define('PERM_TABLE''perm');
    define('PERM_TABLE_ID''id');
    define('PERM_TABLE_NAME''name');
    define('USAGE_ASSIGN_TABLE''usage_assign');
    define('USAGE_ASSIGN_TABLE_USAGE_ID''usage_id');
    define('USAGE_ASSIGN_TABLE_ROLE_ID''role_id');
    define('PERM_ASSIGN_TABLE''perm_assign');
    define('PERM_ASSIGN_TABLE_ROLE_ID''role_id');
    define('PERM_ASSIGN_TABLE_PERM_ID''perm_id');

    class 
    PearAuthorisationStorage {
        var 
    $_connection;
        
        function 
    PearAuthorisationStorage($connection_string) {
            
    $this->_connection = &$this->_createConnection($connection_string);
        }
        function 
    createPermissionsFinder() {
            return new 
    PearPermissionsFinder($this->_connection);
        }
        function &
    createEdit() {
            return new 
    PearAuthorisationEdit($this->_connection);
        }
        function &
    _createConnection($connection_string) {
            return 
    DB::connect($connection_string);
        }
    }

    // Data Mapper/Finder for Permissions object (Domain Object)
    class PearPermissionsFinder {
        var 
    $_connection;

        function 
    PearPermissionsFinder(&$connection) {
            
    $this->_connection =& $connection;
        }

        function &
    findAllByName($name) {
            
    $sql "SELECT p." PERM_TABLE_NAME " AS permission 
                    FROM
                      " 
    USAGE_ASSIGN_TABLE " ua, 
                      " 
    PERM_ASSIGN_TABLE " pa, 
                      " 
    PERM_TABLE " p,
                      " 
    USAGE_TABLE " u 
                    WHERE u." 
    USAGE_TABLE_NAME "='$name' AND
                          ua." 
    USAGE_ASSIGN_TABLE_USAGE_ID "=u." USAGE_TABLE_ID " AND 
                          ua." 
    USAGE_ASSIGN_TABLE_ROLE_ID "pa." PERM_ASSIGN_TABLE_ROLE_ID " AND 
                          pa." 
    PERM_ASSIGN_TABLE_PERM_ID "=p." PERM_TABLE_ID;

            
    $result =& $this->_connection->query($sql);

            
    $all = array();
            while (
    $row =& $result->fetchRow(DB_FETCHMODE_ASSOC)) {
                
    $all[] = $row['permission'];
            }

            
    $result->free();

            return new 
    Permissions($all);
        }
    }
    Comments are welcome (appreciated)

    JT
    Last edited by seratonin; Apr 13, 2004 at 09:14.

  16. #66
    SitePoint Evangelist
    Join Date
    Dec 2003
    Location
    Arizona
    Posts
    411
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    PearRoleAssignmentGateway implementation:

    PHP Code:
    <?php
    define
    ('ROLE_ASSIGN_TABLE''role_assign');
    define('ROLE_ASSIGN_TABLE_ROLE_ID''role_id');
    define('ROLE_ASSIGN_TABLE_USAGE_ID''usage_id');

    // Will be used by PearAuthorisationEdit::assign($name, $role)
    class PearRoleAssignmentGateway {
        var 
    $_connection;
        var 
    $_roleId;
        var 
    $_usageId;

        function 
    PearRoleAssignmentGateway(&$connection$roleId 0$usageId 0) {
            
    $this->_connection =& $connection;
            
    $this->_roleId $roleId;
            
    $this->_usageId $usageId;
        }

        function 
    setRoleId($roleId) {
            
    $this->_roleId $roleId;
        }

        function 
    setUsageId($usageId) {
            
    $this->_usageId $usageId;
        }
     
        function 
    insert() {
            
    $this->_connection->query("INSERT INTO " ROLE_ASSIGN_TABLE "VALUES (" $this->_usageId ", " $this->_roleId ")");
        }

        function 
    delete() {
            
    $this->_connection->query("DELETE FROM " ROLE_ASSIGN_TABLE " WHERE " ROLE_ASSIGN_TABLE_ROLE_ID "=" $this->_roleId " AND " ROLE_ASSIGN_TABLE_USAGE_ID "=" $this->_usageId);
        }
    }
    ?>
    PearPermissionAssignmentGateway implementation:

    PHP Code:
    <?php
    define
    ('PERM_ASSIGN_TABLE''perm_assign');
    define('PERM_ASSIGN_TABLE_PERM_ID''perm_id');
    define('PERM_ASSIGN_TABLE_ROLE_ID''role_id');

    // Will be used by PearAuthorisationEdit::permit($role, $action)
    class PearPermissionAssignmentGateway {
        var 
    $_connection;
        var 
    $_permissionId;
        var 
    $_roleId;

        function 
    PearPermissionAssignmentGateway(&$connection$permissionId 0$roleId 0) {
            
    $this->_connection =& $connection;
            
    $this->_permissionId $permissionId;
            
    $this->_roleId $roleId;
        }

        function 
    setPermissionId($permissionId) {
            
    $this->_permissionid $permissionId;
        }

        function 
    setRoleId($roleId) {
            
    $this->_roleId $roleId;
        }
     
        function 
    insert() {
            
    $this->_connection->query("INSERT INTO " PERM_ASSIGN_TABLE " VALUES (" $this->_permissionId ", " $this->_roleId ")");
        }

        function 
    delete() {
            
    $this->_connection->query("DELETE FROM " PERM_ASSIGN_TABLE " WHERE " PERM_ASSIGN_TABLE_PERM_ID "=" $this->_permissionId " AND " PERM_ASSIGN_TABLE_ROLE_ID "=" $this->_roleId);
        }
    }
    PearAuthorisationEdit implementation:

    PHP Code:
    class PearAuthorisationEdit {
        var 
    $_connection;

        function 
    PearAuthorisationEdit(&$connection) {
            
    $this->_connection =& $connection;
        }

        function 
    addUsage($usage) {
            
    $gateway =& PearUsageGateway($this->_connection);
            
    $gateway->setName($usage);
            
    $gateway->insert();
        }

        function 
    dropUsage($usage) {
            
    $gateway =& PearUsageGateway($this->_connection);
            
    $gateway->setName($usage);
            
    $gateway->delete();
        }

        function 
    addRole($role) {
            
    $gateway =& PearRoleGateway($this->_connection);
            
    $gateway->setName($role);
            
    $gateway->insert();
        }

        function 
    dropRole($role) {
            
    $gateway =& PearRoleGateway($this->_connection);
            
    $gateway->setName($role);
            
    $gateway->delete();
        }

        function 
    addPermission($action) {
            
    $gateway =& PearPermissionGateway($this->_connection);
            
    $gateway->setName($action);
            
    $gateway->insert();
        }

        function 
    dropPermission($action) {
            
    $gateway =& PearPermissionGateway($this->_connection);
            
    $gateway->setName($action);
            
    $gateway->delete();
        }

        
    // Role Assignment (RA): Assign Roles to Usages
        
    function assign($usage$role) {
            
    // Row Data Gateway access for Usage table
            
    $finder =& PearUsageFinder($this->_connection);
            
    $finder->findByName($usage);
            
    $usageId $finder->getUsageId();

            
    // Row Data Gateway access for Role Table
            
    $finder =& PearRoleFinder($this->_connection);
            
    $finder->findByName($role);
            
    $roleId $finder->getRoleId();

            
    // Row Data Gateway access for RoleAssignment Table
            
    $gateway =& new PearRoleAssignmentGateway($this->_connection);
            
    $gateway->setUsageId($usageId);
            
    $gateway->setRoleId($roleId);
            
    $gateway->insert();
        }

        
    // Permission Assignment (PA): Assign permissions to Roles
        
    function permit($role$action) {
            
    // Row Data Gateway access for Role Table
            
    $finder &= PearRoleFinder($this->_connection);
            
    $finder->findByName($role);
            
    $roleId $finder->getRoleId();

            
    // Row Data Gateway access for Permission Table
            
    $finder &= PearPermissionFinder($this->_connection);
            
    $finder->findByName($action);
            
    $permissionId $finder->getPermissionId();

            
    // Row Data Gateway access for PermissionAssignment Table
            
    $gateway &= PearPermissionAssignmentGateway($this->_connection);
            
    $gateway->setRoleId($roleId);
            
    $gateway->setPermissionId($permissionId);
            
    $gateway->insert();
        }

    That leaves the following classes to be implemented: PearUsageGateway (setName/insert/delete), PearRoleGateway (setName/insert/delete), PearPermissionGateway (setName/insert/delete), PearUsageFinder (findByName/getUsageId), PearRoleFinder (findByName/getRoleId), and PearPermissionFinder (findByName/getPermissionId).

    I think they should all be pretty trivial to implement as above.

    Thanks,

    JT
    Last edited by seratonin; Apr 13, 2004 at 09:13.

  17. #67
    SitePoint Evangelist
    Join Date
    Dec 2003
    Location
    Arizona
    Posts
    411
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by seratonin
    That leaves the following classes to be implemented: PearUsageGateway (setName/insert/delete), PearRoleGateway (setName/insert/delete), PearPermissionGateway (setName/insert/delete), PearUsageFinder (findByName/getUsageId), PearRoleFinder (findByName/getRoleId), and PearPermissionFinder (findByName/getPermissionId).
    I'm thinking that the Row Data Gateways left to be implemented will take on the following form (attempting to strictly adhere to Fowler's description):

    PHP Code:
    <?php
    define
    ('USAGE_TABLE''usage');
    define('USAGE_TABLE_NAME''name');
    define('USAGE_TABLE_ID''id');

    class 
    PearUsageGateway {
        var 
    $_connection;
        var 
    $_id;
        var 
    $_name;

        function 
    PearUsageGateway(&$connection) {
            
    $this->_connection =& $connection;
        }

        function 
    setName($name) {
            
    $this->_name $name;
        }

        function 
    getName() {
            return 
    $this->_name;
        }

        function 
    setUsageId($id) {
            
    $this->_id $id;
        }

        function 
    getUsageId() {
            return 
    $this->_id;
        }

        function 
    insert() {
            
    $this->_connection->query("INSERT INTO " USAGE_TABLE " (" USAGE_TABLE_NAME ") VALUES ('" $this->_name "')");
        }

        function 
    update() {
            
    $this->_connection->query("UPDATE " USAGE_TABLE " SET " USAGE_TABLE_NAME "='" $this->_name "' WHERE " USAGE_TABLE_ID "=" $this->_id);
        }

        function 
    delete() {
    //      $this->_connection->query("DELETE FROM " . USAGE_TABLE . " WHERE " . USAGE_TABLE_ID . "=" . $this->_id);

            // delete by name
            
    $this->_connection->query("DELETE FROM " USAGE_TABLE " WHERE " USAGE_TABLE_NAME "='" $this->_name "'");
        }

        function &
    load(&$result) {
            
    $gateway =& new PearUsageGateway($this->_connection);
            
    $gateway->setUsageId($result['id']);
            
    $gateway->setName($result['name']);
            return 
    $gateway;
        }
    }

    class 
    PearUsageFinder {
        var 
    $_connection;

        function 
    PearUsageFinder(&$connection) {
            
    $this->_connection $connection;
        }

        function &
    findByName($name) {
            
    $sql "SELECT * FROM " USAGE_TABLE " WHERE " USAGE_TABLE_NAME "='$name'";
            
    $result =& $this->_connection->query($sql);
            return 
    PearUsageGateway::load($result);
        }
    }
    ?>
    JT
    Last edited by seratonin; Apr 13, 2004 at 09:30.

  18. #68
    ********* Victim lastcraft's Avatar
    Join Date
    Apr 2003
    Location
    London
    Posts
    2,423
    Mentioned
    2 Post(s)
    Tagged
    0 Thread(s)
    Hi...

    Quote Originally Posted by seratonin
    It seems that since we are not really using a DomainModel here
    We seemed to have dropped below that level. We do have domain objects above, the Permissions object. Authoriser is technical, but an application can subclass it into the domain...
    PHP Code:
    class LoginAuthoriser extends Authoriser {
        function 
    LoginAuthoriser() {
            
    $this->Authoriser(new PearAuthorisationStorage());
        }
        function &
    createEdit() {
            return new 
    LoginEdit(parent::createEdit());
        }

    Normally the config. file would be brought in here, but you get the idea.

    The LoginEdit can expose just domain significant operations, such as addPublicUser() or addAuthor() and build them from addUsage(), etc. and commit().

    That makes the third domain class and I quite like it this way .

    Quote Originally Posted by seratonin
    that a the TableGateway pattern will work better than a DataMapper (especially in the PearAuthorisationEdit class).
    Agree 100%. Because the model is held on the database and is quite complicated and because we are exposing a data structure, we have the situation where we are doing incremental edits. DataMapper just doesn't seem to work with anything less a complete domain concept. Hm...interesting.

    yours, Marcus
    Marcus Baker
    Testing: SimpleTest, Cgreen, Fakemail
    Other: Phemto dependency injector
    Books: PHP in Action, 97 things

  19. #69
    SitePoint Evangelist
    Join Date
    Dec 2003
    Location
    Arizona
    Posts
    411
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I fixed some SQL and removed "update" function from UserAssignmentGateway and PermissionAssignmentGateway.

    JT

  20. #70
    SitePoint Zealot
    Join Date
    Feb 2003
    Location
    Virginia
    Posts
    143
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Hi everyone.

    I've had some time to read what is being proposed in regard to design and implementation.

    It seems to me that everyone is moving somewhat too quickly. The reason I say this is that no one has really discussed what the design requirements of the data structure are and how the application will benefit from one layout versus another.


    1) Before we jump into a implementation that may not fulfill the requirements we seek I propose we discuss the design of the Data Structures (session and disk). Does anyone agree?

    2) One of the issues I ran into in designing a Authorization model is that of the framework and it's structure. I think this was overlooked. What are the requirements of the framework - if there is one?

    3) What are the general design goals that we wish to accomplish? It may be prudent to list them, though they may be well known, so that we can simply mark them off as accomplished.


    I want to keep it simple - for me. I know this is a vast project and it could easily become confusing as to where exactly we are headed, utility wise.

    Great work so far!

    Can't wait to hear what you all think, I'm all ready to dive in.

  21. #71
    ********* Victim lastcraft's Avatar
    Join Date
    Apr 2003
    Location
    London
    Posts
    2,423
    Mentioned
    2 Post(s)
    Tagged
    0 Thread(s)
    Hi...

    Quote Originally Posted by Resolution
    1) Before we jump into a implementation that may not fulfill the requirements we seek I propose we discuss the design of the Data Structures (session and disk). Does anyone agree?
    I think it is just a case of start the ball rolling and see who runs with it . Actually I think we have managed to hide the data structures to a large degree as the PearAuthorisationStorage is one of many possibilities. What did you have in mind?

    Quote Originally Posted by Resolution
    I think this was overlooked. What are the requirements of the framework - if there is one?
    Minimal enough to fit in a tutorial, which means smallest possible scope. Storage/schema nuetral and remotable are what we have ended up with. It was kind of described in a few posts, a starting test case punted as the spec. and top down from there.

    Quote Originally Posted by Resolution
    ICan't wait to hear what you all think, I'm all ready to dive in.
    It's open house - dive in .

    yours, Marcus
    Marcus Baker
    Testing: SimpleTest, Cgreen, Fakemail
    Other: Phemto dependency injector
    Books: PHP in Action, 97 things

  22. #72
    ********* Victim lastcraft's Avatar
    Join Date
    Apr 2003
    Location
    London
    Posts
    2,423
    Mentioned
    2 Post(s)
    Tagged
    0 Thread(s)
    Hi...

    Quote Originally Posted by seratonin
    I fixed some SQL and removed "update" function from UserAssignmentGateway and PermissionAssignmentGateway.
    You have certainly churned through this at a terrific rate . As much as to examine all sides as anything else, here are contradictory thoughts in no order. LIke a whiteboard discussion with no whiteboard...

    1) Do we have two layers of abstraction here? That is the TableGateways and the Storage (basically DataAccessor). Could everything just be coded straight into the storage classes?

    2) Need transactions of course.

    3) Some stuff looks repetitive now. Can we extract some commonality into a gateway base class?

    4) For file based approaches that are written and read in one go, it makes sense to use a data mapper. We haven't implemented this yet, but I can see how it would work out. Load (constructor) becomes parsing and commit() becomes marshalling. One model and lot's of mappers would cover different file formats, all hidden behind teh storage classes.

    5) In what other ways can the code now be made smaller and simpler?

    6) Does the test case still run?

    7) Do we need database IDs? Can we not just use the strings as keys for these tables?

    8) If we try to assign() or permit() with missing roles/actions should these be created automatically? It would certainly make the client interface easier and we wouldn't have to deal with so much error handling. The is an OO principle called "tell, don't ask". We just tell the admin. interface to add user's and roles and not have to ask it whether they exist already.

    yours, Marcus
    Marcus Baker
    Testing: SimpleTest, Cgreen, Fakemail
    Other: Phemto dependency injector
    Books: PHP in Action, 97 things

  23. #73
    SitePoint Evangelist
    Join Date
    Dec 2003
    Location
    Arizona
    Posts
    411
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Realistically, there is only a little bit left to implement. I was hoping that we could finish and get it working before we discuss the shortcomings of the solution. We only have RoleGateway, RoleFinder, PermissionGateway, and PermissionFinder left to code and then we can run your tests. Once we get it working in its current state we can iterate and refactor. What do you think about this approach?

    Thanks,

    JT

  24. #74
    SitePoint Evangelist
    Join Date
    Dec 2003
    Location
    Arizona
    Posts
    411
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    PearRoleGateway and PearRoleFinder implementations:

    PHP Code:
    <?php
    define
    ('ROLE_TABLE''role');
    define('ROLE_TABLE_NAME''name');
    define('ROLE_TABLE_ID''id');

    class 
    PearRoleGateway 
        var 
    $_connection
        var 
    $_id
        var 
    $_name

        function 
    PearRoleGateway(&$connection) { 
            
    $this->_connection =& $connection
        } 

        function 
    setName($name) { 
            
    $this->_name $name
        } 

        function 
    getName() { 
            return 
    $this->_name
        } 

        function 
    setRoleId($id) { 
            
    $this->_id $id
        } 

        function 
    getRoleId() { 
            return 
    $this->_id
        } 

        function 
    insert() { 
            
    $this->_connection->query("INSERT INTO " ROLE_TABLE " (" ROLE_TABLE_NAME ") VALUES ('" $this->_name "')"); 
        } 

        function 
    update() { 
            
    $this->_connection->query("UPDATE " ROLE_TABLE " SET " ROLE_TABLE_NAME "='" $this->_name "' WHERE " ROLE_TABLE_ID "=" $this->_id); 
        } 

        function 
    delete() {
            
    // delete by name 
            
    $this->_connection->query("DELETE FROM " ROLE_TABLE " WHERE " ROLE_TABLE_NAME "='" $this->_name "'"); 
        } 

        function &
    load(&$result) { 
            
    $gateway =& new PearRoleGateway($this->_connection); 
            
    $gateway->setRoleId($result['id']); 
            
    $gateway->setName($result['name']); 
            return 
    $gateway;
        } 
    }

    class 
    PearRoleFinder {
        var 
    $_connection;

        function 
    PearRoleFinder(&$connection) {
            
    $this->_connection =& $connection;
        }

        function &
    findByName($name) {
            
    $sql "SELECT * FROM " ROLE_TABLE " WHERE " ROLE_TABLE_NAME "='$name'";
            
    $result &= $this->_connection->query($sql);
            return 
    PearRoleGateway::load($result);
        }
    }
    ?>
    PearPermissionGateway and PearPermissionFinder implementations:

    PHP Code:
    <?php
    define
    ('PERMISSION_TABLE''role');
    define('PERMISSION_TABLE_NAME''name');
    define('PERMISSION_TABLE_ID''id');

    class 
    PearPermissionGateway 
        var 
    $_connection
        var 
    $_id
        var 
    $_name

        function 
    PearPermissionGateway(&$connection) { 
            
    $this->_connection =& $connection
        } 

        function 
    setName($name) { 
            
    $this->_name $name
        } 

        function 
    getName() { 
            return 
    $this->_name
        } 

        function 
    setPermissionId($id) { 
            
    $this->_id $id
        } 

        function 
    getPermissionId() { 
            return 
    $this->_id
        } 

        function 
    insert() { 
            
    $this->_connection->query("INSERT INTO " PERMISSION_TABLE " (" PERMISSION_TABLE_NAME ") VALUES ('" $this->_name "')"); 
        } 

        function 
    update() { 
            
    $this->_connection->query("UPDATE " PERMISSION_TABLE " SET " PERMISSION_TABLE_NAME "='" $this->_name "' WHERE " PERMISSION_TABLE_ID "=" $this->_id); 
        } 

        function 
    delete() {
            
    // delete by name 
            
    $this->_connection->query("DELETE FROM " ROLE_TABLE " WHERE " PERMISSION_TABLE_NAME "='" $this->_name "'"); 
        } 

        function &
    load(&$result) { 
            
    $gateway =& new PearPermissionGateway($this->_connection); 
            
    $gateway->setPermissionId($result['id']); 
            
    $gateway->setName($result['name']); 
            return 
    $gateway;
        } 
    }

    class 
    PearPermissionFinder {
        var 
    $_connection;

        function 
    PearPermissionFinder(&$connection) {
            
    $this->_connection =& $connection;
        }

        function &
    findByName($name) {
            
    $sql "SELECT * FROM " PERMISSION_TABLE " WHERE " PERMISSION_TABLE_NAME "='$name'";
            
    $result &= $this->_connection->query($sql);
            return 
    PearPermissionGateway::load($result);
        }
    }
    ?>
    Okay. I believe those are the last 4 classes to implement before we can run the test cases. I can definitely see a lot of repetition. I think that this is actually good, it means that we have something to refactor and improve in our next iteration. I guess I will put all this code together and try to give it a go.

    Thanks,

    JT

  25. #75
    SitePoint Evangelist
    Join Date
    Dec 2003
    Location
    Arizona
    Posts
    411
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I got everything setup and got all of the test cases to pass except for "testBadPasswordHasNothingAllowed". (SimpleTest is a nice tool, BTW. I had never used it before this.)

    Here is the working code for our first iteration:

    In file "rbac.sql":

    Code:
    create table `usage` (
        id int(11) not null auto_increment primary key,
        name varchar(255)
    );
    
    create table role (
        id int(11) not null auto_increment primary key,
        name varchar(255)
    );
    
    create table perm (
        id int(11) not null auto_increment primary key,
        name varchar(255)
    );
    
    create table usage_assign (
        usage_id int(11) not null default 0,
        role_id int(11) not null default 0,
        primary key(usage_id, role_id)
    );
    
    create table perm_assign (
        role_id int(11) not null default 0,
        perm_id int(11) not null default 0,
        primary key(role_id, perm_id)
    );
    In file "authoriser.php":

    PHP Code:
    <?php
    require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR 'permissions.php');
    require_once(
    dirname(__FILE__) . DIRECTORY_SEPARATOR 'cache.php');

    class 
    Authoriser {
        var 
    $_storage;

        function 
    Authoriser(&$storage) {
            
    $this->_storage = &$storage;
        }
        function &
    getPermissions($name) {
            
    $finder = &new PermissionsCache($this->_storage->createPermissionsFinder());
            return new 
    Permissions($finder->findAllByName($name));
        }
        function &
    createEdit() {
            return 
    $this->_storage->createEdit();
        }
    }
    ?>
    In file "cache.php":

    PHP Code:
    <?php
    class PermissionsCache {
        var 
    $_finder;

        function 
    PermissionsCache(&$finder) {
            
    $this->_finder = &$finder;
        }
        function &
    findAllByName($name) {
            return 
    $this->_finder->findAllByName($name);
        }
    }
    ?>
    In file "pear_storage.php":

    PHP Code:
    <?php
    require_once("DB.php");
    require_once(
    "permissions.php");

    define('USAGE_TABLE''`usage`');
    define('USAGE_TABLE_ID''id');
    define('USAGE_TABLE_NAME''name');

    define('ROLE_TABLE''role');
    define('ROLE_TABLE_ID''id');
    define('ROLE_TABLE_NAME''name');

    define('PERM_TABLE''perm');
    define('PERM_TABLE_ID''id');
    define('PERM_TABLE_NAME''name');

    define('USAGE_ASSIGN_TABLE''usage_assign');
    define('USAGE_ASSIGN_TABLE_USAGE_ID''usage_id');
    define('USAGE_ASSIGN_TABLE_ROLE_ID''role_id');

    define('PERM_ASSIGN_TABLE''perm_assign');
    define('PERM_ASSIGN_TABLE_ROLE_ID''role_id');
    define('PERM_ASSIGN_TABLE_PERM_ID''perm_id');

    class 
    PearAuthorisationStorage {
        var 
    $_connection;
        
        function 
    PearAuthorisationStorage($connection_string) {
            
    $this->_connection = &$this->_createConnection($connection_string);
        }

        function 
    createPermissionsFinder() {
            return new 
    PearPermissionsFinder($this->_connection); 
        }

        function &
    createEdit() {
            return new 
    PearAuthorisationEdit($this->_connection);
        }

        function &
    _createConnection($connection_string) {
            
    $db =& DB::connect($connection_string);
            
            if (
    DB::isError($db)) {
                die(
    $db->getMessage());
            }

            return 
    $db;
        }
    }

    class 
    PearAuthorisationEdit {
        var 
    $_connection;

        function 
    PearAuthorisationEdit(&$connection) {
            
    $this->_connection =& $connection;
        }

        function 
    addUsage($usage) {
            
    $gateway =& new PearUsageGateway($this->_connection);
            
    $gateway->setName($usage);
            
    $gateway->insert();
        }
        
        function 
    dropUsage($usage) {
            
    $gateway =& new PearUsageGateway($this->_connection);
            
    $gateway->setName($usage);
            
    $gateway->delete();
        }

        function 
    addRole($role) {
            
    $gateway =& new PearRoleGateway($this->_connection);
            
    $gateway->setName($role);
            
    $gateway->insert();
        }

        function 
    dropRole($role) {
            
    $gateway =& new PearRoleGateway($this->_connection);
            
    $gateway->setName($role);
            
    $gateway->delete();
        }

        function 
    addPermission($action) {
            
    $gateway =& new PearPermissionGateway($this->_connection);
            
    $gateway->setName($action);
            
    $gateway->insert();
        }

        function 
    dropPermission($action) {
            
    $gateway =& new PearPermissionGateway($this->_connection);
            
    $gateway->setName($action);
            
    $gateway->delete();
        }

        
    // Usage Assignment: Assign Roles to Usages
        
    function assign($usage$role) {
            
    // Row Data Gateway access for Usage table
            
    $finder =& new PearUsageFinder($this->_connection);
            
    $gateway =& $finder->findByName($usage);
            
    $usageId $gateway->getUsageId();

            
    // Row Data Gateway access for Role Table
            
    $finder =& new PearRoleFinder($this->_connection);
            
    $gateway =& $finder->findByName($role);
            
    $roleId $gateway->getRoleId();

            
    // Row Data Gateway access for Usage Assignment Table
            
    $gateway =& new PearUsageAssignmentGateway($this->_connection);
            
    $gateway->setUsageId($usageId);
            
    $gateway->setRoleId($roleId);
            
    $gateway->insert();
        } 

        
    // Permission Assignment (PA): Assign Permissions to Roles
        
    function permit($role$action) {
            
    // Row Data Gateway access for Role Table
            
    $finder =& new PearRoleFinder($this->_connection);
            
    $gateway =& $finder->findByName($role);
            
    $roleId $gateway->getRoleId();

            
    // Row Data Gateway access for Permission Table
            
    $finder =& new PearPermissionFinder($this->_connection);
            
    $gateway =& $finder->findByName($action);
            
    $permissionId $gateway->getPermissionId();

            
    // Row Data Gateway access for Permission Assignment Table
            
    $gateway =& new PearPermissionAssignmentGateway($this->_connection);
            
    $gateway->setRoleId($roleId);
            
    $gateway->setPermissionId($permissionId);
            
    $gateway->insert();
        }
        
        function 
    commit() {
            
    // ...
        
    }
    }

    // Data Mapper/Finder for Permissions object (Domain Object)
    class PearPermissionsFinder {
        var 
    $_connection;

        function 
    PearPermissionsFinder(&$connection) {
            
    $this->_connection =& $connection;
        }

        function &
    findAllByName($name) {
            
    $sql "SELECT p." PERM_TABLE_NAME " AS permission
                    FROM
                      " 
    USAGE_ASSIGN_TABLE " ua,
                      " 
    PERM_ASSIGN_TABLE " pa, 
                      " 
    PERM_TABLE " p,
                      " 
    USAGE_TABLE " u
                    WHERE u." 
    USAGE_TABLE_NAME " = '$name' AND
                          ua." 
    USAGE_ASSIGN_TABLE_USAGE_ID " = u." USAGE_TABLE_ID " AND
                          ua." 
    USAGE_ASSIGN_TABLE_ROLE_ID " = pa." PERM_ASSIGN_TABLE_ROLE_ID " AND
                          pa." 
    PERM_ASSIGN_TABLE_PERM_ID " = p." PERM_TABLE_ID;

            
    $result =& $this->_connection->query($sql);
                    
            if (
    DB::isError($result)) {
                die(
    $result->getMessage());
            }

            
    $all = array();
            while (
    $row =& $result->fetchRow(DB_FETCHMODE_ASSOC)) {
                
    $all[] = $row['permission'];
            }

            
    $result->free();

    //      return new Permissions($all);
            
    return $all;
        }
    }

    class 
    PearUsageAssignmentGateway {
        var 
    $_connection;
        var 
    $_roleId;
        var 
    $_usageId;

        function 
    PearUsageAssignmentGateway(&$connection$roleId 0$usageId 0) {
            
    $this->_connection =& $connection;
            
    $this->_roleId $roleId;
            
    $this->_usageId $usageId;
        }

        function 
    setRoleId($roleId) {
            
    $this->_roleId $roleId;
        }

        function 
    setUsageId($usageId) {
            
    $this->_usageId $usageId;
        }

        function 
    insert() {
            
    $sql "INSERT INTO " USAGE_ASSIGN_TABLE " VALUES (" $this->_usageId ", " $this->_roleId ")";
            
            
    $this->_connection->query($sql);
            
            if (
    DB::isError($this->_connection)) {
                die(
    $this->_connection->getMessage());
            }
        }

        function 
    delete() {
            
    $sql "DELETE FROM " USAGE_ASSIGN_TABLE " WHERE " USAGE_ASSIGN_TABLE_ROLE_ID "=" $this->_roleId " AND " USAGE_ASSIGN_TABLE_USAGE_ID "=" $this->_usageId;
        
            
    $this->_connection->query($sql);

            if (
    DB::isError($this->_connection)) {
                die(
    $this->_connection->getMessage());
            }
        }
    }

    class 
    PearPermissionAssignmentGateway {
        var 
    $_connection;
        var 
    $_permissionId;
        var 
    $_roleId;

        function 
    PearPermissionAssignmentGateway(&$connection$permissionId 0$roleId 0) {
            
    $this->_connection =& $connection;
            
    $this->_permissionId $permissionId;
            
    $this->_roleId $roleId;
        }

        function 
    setPermissionId($permissionId) {
            
    $this->_permissionId $permissionId;
        }

        function 
    setRoleId($roleId) {
            
    $this->_roleId $roleId;
        }

        function 
    insert() {
            
    $sql "INSERT INTO " PERM_ASSIGN_TABLE " VALUES (" $this->_roleId ", " $this->_permissionId ")";
        
            
    $this->_connection->query($sql);
            
            if (
    DB::isError($this->_connection)) {
                die(
    $this->_connection->getMessage());
            }
        }

        function 
    delete() {
            
    $sql "DELETE FROM " PERM_ASSIGN_TABLE " WHERE " PERM_ASSIGN_TABLE_PERM_ID "=" $this->_permissionId " AND " PERM_ASSIGN_TABLE_ROLE_ID "=" $this->_roleId;
        
            
    $this->_connection->query($sql);
            
            if (
    DB::isError($this->_connection)) {
                die(
    $this->_connection->getMessage());
            }
        }
    }

    class 
    PearUsageGateway {
        var 
    $_connection;
        var 
    $_id;
        var 
    $_name;

        function 
    PearUsageGateway(&$connection) {
            
    $this->_connection =& $connection;
        }

        function 
    setName($name) {
            
    $this->_name $name;
        }

        function 
    getName() {
            return 
    $this->_name;
        }

        function 
    setUsageId($id) {
            
    $this->_id $id;
        }

        function 
    getUsageId() {
            return 
    $this->_id;
        }

        function 
    insert() {
            
    $sql "INSERT INTO " USAGE_TABLE " (" USAGE_TABLE_NAME ") VALUES ('" $this->_name "')";
            
    $this->_connection->query($sql);
            if (
    DB::isError($this->_connection)) {
                die(
    $this->_connection->getMessage());
            }
        }

        function 
    update() {
            
    $this->_connection->query("UPDATE " USAGE_TABLE " SET " USAGE_TABLE_NAME "='" $this->_name "' WHERE " USAGE_TABLE_ID "=" $this->_id);
        }

        function 
    delete() {
            
    // delete by name
            
    $this->_connection->query("DELETE FROM " USAGE_TABLE " WHERE " USAGE_TABLE_NAME "='" $this->_name "'");
        }

        function &
    load(&$row) {
            
    $gateway =& new PearUsageGateway($this->_connection);
            
    $gateway->setUsageId($row['id']);
            
    $gateway->setName($row['name']);
            return 
    $gateway;
        }
    }

    class 
    PearUsageFinder {
        var 
    $_connection;

        function 
    PearUsageFinder(&$connection) {
            
    $this->_connection $connection;
        }

        function &
    findByName($name) {
            
    $sql "SELECT * FROM " USAGE_TABLE " WHERE " USAGE_TABLE_NAME "='$name'";
            
    $result =& $this->_connection->query($sql);
            
    $row =& $result->fetchRow(DB_FETCHMODE_ASSOC);
            return 
    PearUsageGateway::load($row);
        }
    }

    class 
    PearRoleGateway {
        var 
    $_connection;
        var 
    $_id;
        var 
    $_name;

        function 
    PearRoleGateway(&$connection) {
            
    $this->_connection =& $connection;
        }

        function 
    setName($name) {
            
    $this->_name $name;
        }

        function 
    getName() {
            return 
    $this->_name;
        }

        function 
    setRoleId($id) {
            
    $this->_id $id;
        }

        function 
    getRoleId() {
            return 
    $this->_id;
        }

        function 
    insert() {
            
    $this->_connection->query("INSERT INTO " ROLE_TABLE " (" ROLE_TABLE_NAME ") VALUES ('" $this->_name "')");
        }

        function 
    update() {
            
    $this->_connection->query("UPDATE " ROLE_TABLE " SET " ROLE_TABLE_NAME "='" $this->_name "' WHERE " ROLE_TABLE_ID "=" $this->_id);
        }

        function 
    delete() {
            
    // delete by name
            
    $this->_connection->query("DELETE FROM " ROLE_TABLE " WHERE " ROLE_TABLE_NAME "='" $this->_name "'");
        }

        function &
    load(&$row) { 
            
    $gateway =& new PearRoleGateway($this->_connection);
            
    $gateway->setRoleId($row['id']);
            
    $gateway->setName($row['name']);
            return 
    $gateway;
        }
    }

    class 
    PearRoleFinder {
        var 
    $_connection;

        function 
    PearRoleFinder(&$connection) {
            
    $this->_connection =& $connection;
        }

        function &
    findByName($name) {
            
    $sql "SELECT * FROM " ROLE_TABLE " WHERE " ROLE_TABLE_NAME "='$name'";
            
    $result =& $this->_connection->query($sql);
            
    $row =& $result->fetchRow(DB_FETCHMODE_ASSOC);
            return 
    PearRoleGateway::load($row);
        }
    }

    class 
    PearPermissionGateway {
        var 
    $_connection;
        var 
    $_id;
        var 
    $_name;

        function 
    PearPermissionGateway(&$connection) {
            
    $this->_connection =& $connection;
        }

        function 
    setName($name) {
            
    $this->_name $name;
        }

        function 
    getName() {
            return 
    $this->_name;
        }

        function 
    setPermissionId($id) {
            
    $this->_id $id;
        }

        function 
    getPermissionId() {
            return 
    $this->_id;
        }

        function 
    insert() {
            
    $this->_connection->query("INSERT INTO " PERM_TABLE " (" PERM_TABLE_NAME ") VALUES ('" $this->_name "')");
        }

        function 
    update() {
            
    $this->_connection->query("UPDATE " PERM_TABLE " SET " PERM_TABLE_NAME "='" $this->_name "' WHERE " PERM_TABLE_ID "=" $this->_id);
        }

        function 
    delete() {
            
    // delete by name
            
    $this->_connection->query("DELETE FROM " PERM_TABLE " WHERE " PERM_TABLE_NAME "='" $this->_name "'");
        }

        function &
    load(&$row) {
            
    $gateway =& new PearPermissionGateway($this->_connection);
            
    $gateway->setPermissionId($row['id']);
            
    $gateway->setName($row['name']);
            return 
    $gateway;
        }
    }

    class 
    PearPermissionFinder {
        var 
    $_connection;

        function 
    PearPermissionFinder(&$connection) {
            
    $this->_connection =& $connection;
        }

        function &
    findByName($name) {
            
    $sql "SELECT * FROM " PERM_TABLE " WHERE " PERM_TABLE_NAME "='$name'";
            
    $result =& $this->_connection->query($sql);
            
    $row =& $result->fetchRow(DB_FETCHMODE_ASSOC);
            return 
    PearPermissionGateway::load($row);
        }
    }
    ?>
    In file "permissions.php":

    PHP Code:
    <?php
    class Permissions {
        var 
    $_all;

        function 
    Permissions(&$all) {
            
    $this->_all =& $all;
        }

        function 
    can($action) {
            return 
    in_array($action$this->_all);
        }
    }
    ?>
    In file "test.php":

    PHP Code:
    <?php
    if (!defined(SIMPLE_TEST)) {
        
    define('SIMPLE_TEST''../simpletest/');
    }

    define('CONNECTION_STRING''mysql://user:pass@localhost/rbac');

    require_once(
    SIMPLE_TEST 'unit_tester.php');
    require_once(
    SIMPLE_TEST 'reporter.php');

    require_once(
    'authoriser.php');
    require_once(
    'pear_storage.php');

    class 
    RoleBasedPermissionsTest extends UnitTestCase {
        function 
    RoleBasedPermissionsTest() {
            
    $this->UnitTestCase();
            
    $this->storage = &new PearAuthorisationStorage(CONNECTION_STRING);
        }
        function 
    setUp() {
            
    $authoriser = &new Authoriser($this->storage);
            
    $edit = &$authoriser->createEdit();
            
    $edit->addUsage('fred');
            
    $edit->addRole('pleb');
            
    $edit->addPermission('do_stuff');
            
    $edit->assign('fred''pleb');
            
    $edit->permit('pleb''do_stuff');
            
    $edit->commit();
        }
        
        function 
    tearDown() {
            
    $authoriser = &new Authoriser($this->storage);
            
    $edit = &$authoriser->createEdit();
            
    $edit->dropUsage('fred');
            
    $edit->dropRole('pleb');
            
    $edit->dropPermission('do_stuff');
            
    $edit->commit();
        }
        
        function 
    testNonUserHasNothingAllowed() {
            
    $authoriser = &new Authoriser($this->storage);
            
    $permissions = &$authoriser->getPermissions('public');
            
    $this->assertFalse($permissions->can('do_stuff'));
        }
        
        
    /*
        function testBadPasswordHasNothingAllowed() {
            $authoriser = &new Authoriser($this->storage);
            $permissions = &$authoriser->getPermissions('fred');
            $this->assertFalse($permissions->can('do_stuff'));
        }
        */
        
        
    function testLegitimateUserHasActionAllowed() {
            
    $authoriser = &new Authoriser($this->storage);
            
    $permissions = &$authoriser->getPermissions('fred');
            
    $this->assertTrue($permissions->can('do_stuff'));
        }
        
        function 
    testUserCannotDoNonAction() {
            
    $authoriser = &new Authoriser($this->storage);
            
    $permissions = &$authoriser->getPermissions('fred');
            
    $this->assertFalse($permissions->can('do_unknown'));
        }
    }

    $test =& new RoleBasedPermissionsTest();
    $test->run(new HtmlReporter());
    ?>
    Obviously we have a long way to go with this. I can already see several refactorings. Also, we need to cascade deletes if a user, role, or pemrission is dropped. Which means the row data gateway isn't sufficient for the two connective tables (usage_asisgn and perm_assign). We will need to use a Table Data Gateway to manage multiple rows on cascading deletes. Anyways, try setting it up and see if you can get similar results.

    Thanks,

    JT
    Last edited by seratonin; Apr 14, 2004 at 09:43.


Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •