Zend_Acl modular class system - zend-framework

Hey guys, I'm starting with Zend, and trying to understand the way it works (getting there), and with Acl classes, people seem to declare all the roles and resources in one file. Now to me this seems a bit of a waste of system resources if the person is only logging in as a basic user, or even just a guest/visitor to the site. So I was thinking is it possible to have different classes which set the roles dependant on the role/resource of the current user.
My current idea is to use switch/case on the role in the bootstrap, and load individual Acl classes based on the role name (for example sake, 'visitor' => 'Model_VisitorAcl', 'users' => 'Model_UserAcl' and 'admin' => 'Model_AdminAcl'), each with a corresponding class file. Then within the class files do something for each one. This is working, and currently I could define all my 'admin' acls in one (backtracking through the rights heirarchy of admin-user-visitor), then in 'user' have 'user'-'visitor' roles and resources.
However this doesn't seem the best way, as if I wanted to change the role permissions for 'user' I'd need to change it for 'user' and 'admin'. This is obv not a problem with 2 roles, but on bigger systems it obviously is.
So I tried something like
class Model_AdminAcl extends Zend_Acl{
function __construct(){
$d = new Model_UserAcl();
//defining the admin role and permissions, with inheritance from the roles
//set in Model_UserAcl
}
}
To try and get the Model_UserAcl construct function to run, thus setting the role 'user', however this does not seem to work, throwing "Uncaught exception 'Zend_Acl_Role_Registry_Exception' with message 'Parent Role id 'users' does not exist'"
error.
So whats the next step? Having individual include files for each module, and have the required classes include them as needed?
Or am I just making mountain out of molehills with this issue?
Thanks,
Psy

Ok, solved this, not sure why I didn't think to try this in the first place (Friday brain frazzle I think)
I just put the contents of the constructor into its own function, and then called that function in the constructor, and make the classes inherit from the class below it.
//Model/VisitorAcl.php
class Model_VisitorAcl extends Zend_Acl{
function __construct(){
$this->addVisitorRules();
}
function addVisitorRules(){
//adding the role and permissions
}
}
//Model/UserAcl.php
class Model_UserAcl extends Model_VisitorAcl{
function __construct(){
$this->addVisitorRules();
$this->addUserRules();
}
function addUserRules(){
//adding the role and permissions
}
}
//Model/AdminAcl.php
class Model_AdminAcl extends Model_AdminAcl{
function __construct(){
$this->addVisitorRules();
$this->addUserRules();
$this->addAdminRules();
}
function addAdminRules(){
//adding the role and permissions
}
}
If this is not the way I should go about it, then by all means please let me know.
Psy

Related

Laravel user forms and admin forms: how to tackle?

Just a question to poll how you guys would tackle this in Laravel:
I have a user preferences page defined in UserController.php which creates the view at user/preferences.blade.php.
Administrators should of course be able to edit the user's preferences and have some extra administrative fields shown to be changed. Furthermore I'd like to collect all admin functionality concerning users in a separate controller called AdminUserController.php
I'm thinking of some possibilities to achieve this functionality:
Create an additional view (e.g. admin/user/preferences.blade.php) and almost replicate the GET and POST methods of UserController.php to accommodate the extra fields. However this seems to me like a lot of redundant code...
Convert the GET and POST methods of UserController.php to something like this:
public function postPreferences($user = NULL, $admin = FALSE) {
if (!isset($user)) $user = Auth::user();
// Process regular fields.
if ($admin) {
// Process admin fields.
}
}
Add the admin fields to user/preference.blade.php and conditionally show them if $admin is TRUE, and then call the UserController's methods from within AdminUserController, e.g.:
public function postPreferences($user) {
return (new UserController)->postPreferences($user, TRUE);
}
However, there are some drawbacks. First: controllers shouldn't call each other... Second: this only works for the POST method. Upon requesting the GET method from UserController an exception is being thrown...
I'm curious about how you would tackle this!
Thanks.
This is mostly a question of preference, but I really suggest you to completely separate all that is possible here. Administration is a process that is very sensitive and not in any way should it be possible, that a normal user will be able to see it under any circumstance.
I believe that we all are humans and we make mistakes more or less often, that's why we need to make sure that our mistakes in not assigning right value to the right variable or making a type of = instead of == not to ruin business logic.
I think you should make a separate view and a separate controller for user self management and administration and never tie them up. If you want to keep your code DRY as much as possible, you may extend your user controller and model from your admin controller and model, but not the other way around. That's just my 2 cents, it all depends on what type of application you are working on and what the stakes are.
<?php
class AdminController extends UserController
{
public function __construct(AdminModel $model)
{
// Use dependency injection
$this->model = $model;
}
// In the original UserController class:
public function postPreferences($user) {
$this->model->edit($user, Input::all());
// you may do it this way so your user only saves user data and
// you admin model saves all the data including administrative
}
// ...
}

Getting a navigation property populated when using a parent class to fetch

I am using a membership provider that specifies required fields in user such as username and password. You can then inherit this user class to create your own user with any other fields you need.
I added an account like so
public Account AdminAccount { get; set; }
when i now get my user using the membershipprovider it does this:
return _context.Set<TUser>().SingleOrDefault(u => u.Username == username);
The class definition for this membership class looks like so
public class FlexMembershipUserStore<TUser>
: IFlexUserStore
where TUser: class, IFlexMembershipUser, new()
So I send in my user class when creating it, and the class knows about Tuser as a IFlexMembershipUser.
However IFlexMembershipUser does not have my AdminAccount property, only my class that inherits from IFlexMembershipUser has that.
And so when I fetch my user even tho he in the database has a field called AdminAccount_Id which is set correctly I only get AdminAccount to be null.
I see the problem of course with the membership only seeing IFlexMembershipUser and my property not existing on that class but other scalar values are read in properly.
Is there anything I can do about it.
In this particular case I can make the account keep track of it's admins as a user list instead. So I can solve this that way but I am still curious if this can be solved otherwise.
The issue here is that EF only loads things you tell it to for performance reasons. You have two options for loading the remote nav properties, Eager loading (my prefered) or lazy loading.
With Eager loading you tell EF at query time you are interested in the specific nav property and it will go ahead and load it for you. To do this:
using System.Data.Entity;
....
_context.Set<TUser>().Include(u=>u.AdminAccount).SingleOrDefault(u => u.Username == username);

ACL implementation based on variable and not static roles

I would like to use Zend's ACL (Zend\Permissions\Acl) not (only) based on static roles but also on (variable) user points.
In my application every user has points. A resource has a minimum of points needed to view it. Access to a resource should be based on the number of points the user currently has.
Example
Resources:
Resource 1: 20 points
Resource 2: 100 points
Resource 3: 150 points
Users:
User 1: 70 points => Access to resource 1
User 2: 135 points => Access to resources 1, 2
User 3: 170 points => Access to resources 1, 2, 3
What would be the best way to do this?
My thoughts so far
Create ACL object dynamically for the currently logged in user based on his points (set each $acl->allow() based on points). This isn't clean.
Create a generic ACL and somehow pass the user's points (I managed to do it with assertions. See my answer below.)
Some (possibly easier/cleaner) way suggested here...
I would greatly appreciate a push in the right direction :)
So this is not just about Zend but working with ACLs in general.
Usually when you implement access rights in an ACL you assign it to a group rather than an individual user. Then you can easily (and dynamically) add or remove users from groups.
In Zend ACL you can think of these groups as the roles. In your case you assign the access rights for a resource to a group (or role) that represent a certain number of points. Now you only have to worry about moving users between these groups based on the points they have earned.
Okay, I tried to implement it myself. Maybe it's not pretty, but it's the best solution I came up with myself. Is this the right direction? I would appreciate any feedback!
Solution:
Instead of strings as resources and roles i use my models (suggested here). I use PointResourceInterface to mark resources that require a specific number of points and implement Zend\Permissions\Acl\Role\RoleInterface in my user class. Now I create a new NeededPointsAssertion:
class NeededPointsAssertion implements AssertionInterface
{
public function assert(Acl $acl, RoleInterface $role = null,
ResourceInterface $resource = null, $privilege = null) {
// Resource must have points, otherwise not applicable
if (!($resource instanceof PointResourceInterface)) {
throw new Exception('Resource is not an PointResourceInterface. NeededPointsAssertion is not applicable.');
}
//check if points are high enough, in my app only users have points
$hasEnoughPoints = false;
if ($role instanceof User) {
// role is User and resource is PointResourceInterface
$hasEnoughPoints = ($role->getPoints() >= $resource->getPoints());
}
return $hasEnoughPoints;
}
}
PointResourceInterface looks like this:
use Zend\Permissions\Acl\Resource\ResourceInterface;
interface PointResourceInterface extends ResourceInterface {
public function getPoints();
}
Setup:
$acl->allow('user', $pointResource, null, new NeededPointsAssertion());
Users have access to resources that need points. But additionally the NeededPointsAssertion is checked.
Access:
I'm checking whether access is allowed like this:
$acl->isAllowed($role, $someResource);
If there's a user $role = $user otherwise it's guest or something else.
Inspiration is from http://www.aviblock.com/blog/2009/03/19/acl-in-zend-framework/
Update: Looking back at it now, it would have also been possible to add the needed points via the constructor and store it as an attribute. Decide for yourself and what makes sense in your application...

Grails save extend class

how to save class extend in grails?
example i have class user and administrator
class User {
String name
String password
}
class Administrator extends User {
String authoritySelected
}
example in class User i have save "user1",
and then i want to change user1 from class user to class administrator and update authoritySelected
def update(){
def user1 = User.get("user1")
user1.authoritySelected
user1.save(flush:true)
}
and get error :
No such property: authoritySelected for class:User
so, how to save authoritySelected in class User and change that to class Administrator? thanks.
Speaking about the syntax, the code you wrote has no sense. Speaking about design, neither.
May I suggest you to study a bit a OOP before attempting doing this kind of stuff? :)
But let's face the problem you submitted.
First suggestion: don't implement the security system for your application, there's a lot of stuff that can do it for you. One above all: Spring Security plugin.
Second: the code you wrote doesn't work because extending a class is a way to make another class 'son' of the parent. In you example, Administrator is a son of User.
def update(){
def user1 = User.get("user1") // I don't get how this should work, but I'll leave it like this in this example
user1.authoritySelected // you're trying to GET the value a property that doesn't exist in the User class, you should SET something here
user1.save(flush:true)
}
If you want your User to change role, the easiest think is to think at the role not as another class, instead it should be an attribute of the User, so you can change it. Once the instance of a class is created, you can't change it (probably this is not totally true, but you shouldn't).
OK, some code:
class User {
String name
String password
String authority // a property of the class you can change
}
def update(){
def user1 = User.get("user1")
user1.authority = 'Administrator' // change the property on the instance you retrieved
user1.save() // save the instance itself
}
This is still not a good design solution to me, I'm just trying to make you able to see what you're doing wrong.
When you say "and then i want to change user1 from class user to class administrator", what exactly are you trying to do?
You are trying to access a property of an object that doesn't exist in that object. Downcasting simply doesn't work like that. You should instantiate an object of type Administrator in order to save one of its properties after.
if you want to create a USER you have to create an USER's instance, for example:
User u = new User(name: "xpto", password: "xptopass").save(flush:true)
An Administrator is a USER too, but with one more data, the authoritySelected, so if the Administrator extends User, he have the same data like a User too.
Administrator a = new Administrator(name: "xpto", password: "xptopPass", authoritySelected: "ADMIN").save(flush:true)
Attention, Object.get(X) methods needs an ID (Long), "X" would be a Long value, not a String.
http://grails.org/doc/2.3.x/ref/Domain%20Classes/get.html

Can I do more than one form for the same model class in symfony?

Well, imagine that we have a register form of a class Customer and we only ask three fields (name,surname,email) and after, when this user logged first time we want to complete this information.
First, we have in lib/form/doctrine a file called 'CustomerForm.class.php' wich is generated automatic on command line. In this file we 'setup' only 3 fields and validators and if we wanna use we do something like that:
$this->form = CustomerForm();
Second, we create manual another form named 'CustomerFormStep1.class.php' where we can setup for validate the other fields. But when we do..
$this->form = CustomerFormStep1();
it returns error: Fatal error: Class 'CustomerFormStep1' not found
What is wrong?
Thanks.
Assuming you have the form defined as:
class CustomerFormStep1 extends sfForm
or similar (sfFormDoctrine etc), and named correctly like you say (CustomerFormStep1.class.php) and in lib/form, then Symfony should just pick the definition up fine. Did you clear the cache after creating and placing it in the right place? (symfony cc).
Create the new CustomerFormStep1 class as #richsage instructed. Then, in your actions you can write something like:
public function executeLogin(){
//before login
$this->form = new CustomerForm();
}
public function executeLoggedIn(){
$this->form = new CustomerFormStep1();
//other steps
}
Haven't you read the tutorial? Extending forms is perfectly described in context with reh admin generator and can of course be applied to any case.