Admin section in ZendFramework application - zend-framework

I have an application at the moment using Zend_Auth for user access. The site has an admin section where I want one user who has the role of admin in my database to be allowed access when he uses his credentials. Is Zend_Acl the only way to do this? As it seems a little complex for what I want to do or would there be any easier solutions to my problem?
I have had a think about this and I am now wondering if it is possible to have two auth controllers one for users and one for my admin section?

I did something like this recently. Create a front-controller plugin for the admin module that checks the user credential. Something like:
class Admin_Plugin_Auth extends Zend_Controller_Plugin_Abstract
{
public function preDispatch(Zend_Controller_Request_Abstract $request)
{
if ($request->getModuleName() != 'admin'){
return;
}
$auth = Zend_Auth::getInstance();
if (!$auth->hasIdentity()){
// send him to login
}
$user = $auth->getIdentity();
if (!$user->isAdmin()){ // or however you check
// send him to a fail page
}
}
}

I decided to go with the method of having a field of "is_admin" in my database if its set to 1 the user is an admin. I then use this code:
public function init()
{
$this->auth=Zend_Auth::getInstance();
if ($this->auth->getStorage()->read()->is_admin) {
$route = array('controller'=>'admin', 'action'=>'index');
} else {
$route = array('controller'=>'index', 'action'=>'index');
$this->_helper->redirector->gotoRoute($route);
}
}
This redirects the user from the admin area if they are not an admin and allows them access if they are an admin.. A lot easier to implement than ACL for the simple use in my application.

Related

Issues with CurrentUserPropertyBinder it cannot always remember user

I have implemented a CurrentUserPropertyBinder (see below) for a web application using FubuMVC.
public class CurrentUserPropertyBinder : IPropertyBinder
{
private readonly Database _database;
private readonly ISecurityContext _security;
public CurrentUserPropertyBinder(Database database, ISecurityContext security)
{
_database = database;
_security = security;
}
public bool Matches(PropertyInfo property)
{
return property.PropertyType == typeof(User)
&& property.Name == "CurrentUser";
}
public void Bind(PropertyInfo property, IBindingContext context)
{
var currentUser = //check database passing the username to get further user details using _security.CurrentIdentity.Name
property.SetValue(context.Object, currentUser, null);
}
}
When I login to my site, this works fine. The CurrentUserPropertyBinder has all the information it requires to perform the task (i.e. _security.CurrentIdentity.Name has the correct User details in it)
When I try and import a file using fineUploader (http://fineuploader.com/) which opens the standard fileDialog the _security.CurrentIdentity.Name is empty.
It doesn't seem to remember who the user was, I have no idea why. It works for all my other routes but then I import a file it will not remember the user.
Please help! Thanks in Advance
NOTE: We are using FubuMVC.Authentication to authenticate the users
I'm guessing your action for this is excluded from authentication; perhaps it's an AJAX-only endpoint/action. Without seeing what that action looks like, I think you can get away with a simple fix for this, if you've updated FubuMVC.Authentication in the past 3 months or so.
You need to enable pass-through authentication for this action. Out of the box, FubuMVC.Auth only wires up the IPrincipal for actions that require authentication. If you want access to that information from other actions, you have to enable the pass-through filter. Here are some quick ways to do that.
Adorn your endpoint/controller class, this specific action method, or the input model for this action with the [PassThroughAuthentication] attribute to opt-in to pass-through auth.
[PassThroughAuthentication]
public AjaxContinuation post_upload_file(UploadInputModel input) { ... }
or
[PassThroughAuthentication]
public class UploadInputModel { ... }
Alter the AuthenticationSettings to match the action call for pass-through in your FubuRegistry during bootstrap.
...
AlterSettings<AuthenticationSettings>(x => {
// Persistent cookie lasts 3 days ("remember me").
x.ExpireInMinutes = 4320;
// Many ways to filter here.
x.PassThroughChains.InputTypeIs<UploadInputModel>();
});
Check /_fubu/endpoints to ensure that the chain with your action call has the pass-through or authentication filter applied.

Zend_Acl, How to check a user with multiple roles for resource access

i am implementing RBAC for my app, and everything is managed from database.
for example i am storing all resources/permissions in a table called permission , all roles in role table, and another table called role_permission to define which role have access to which resources/permissions.
the purpose for going with this approach is because i want the administrator of the app to create the role and assign the permission to role by himself.
User of the app can have multiple roles for example administrator, supervisor, player, referee etc.
I created a model class for Zend_Acl to add roles and resources and assign permission to it.
Below is what i did.
foreach($this->_roles as $role) {
$this->addRole(new Zend_Acl_Role($role['id']));
}
foreach($this->_permissions as $permmission) {
$this->addResource(new Zend_Acl_Resource($permmission['id']));
}
foreach($this->_rolePermissions as $value) {
$this->allow($value['role_id'], $value['permmission_id']);
}
$this->allow($this->_roleAdmin);
it works fine if i want to check wether a permission exist for a particular role for example by using this code.
echo $acl->isAllowed($role, $permission) ? 'allowed' : 'denied';
however i want to check with multiple roles wether the current permission exist for a user with multiple roles.
how am i supposed to check wether the user with multiple roles such as referee, supervisor has the access to resource create report. with isAllowed() you can only check for permission for only 1 role.
The approach I usually take is to create a class that extends Zend_Acl, and extend the isAllowed() function so it can take my user object as a parameter instead. It then loops through that user's roles performing the check for each one. E.g.:
public function isAllowed($roleOrUser = null, $resource = null, $privilege = null)
{
if ($roleOrUser instanceof Users_Model_User) {
// check each of that user's roles
foreach ($roleOrUser->roles as $role) {
if (parent::isAllowed($role, $resource, $privilege)) {
return true;
}
}
return false;
} else {
return parent::isAllowed($roleOrUser, $resource, $privilege);
}
}

In Zend, is there a better way to check if a user hasIdentity()?

Currently, I am using Zend_Auth::getInstance()->hasIdentity() to check if my user is logged in in every controller that requires a login. I feel like I am practicing Zend horribly, so I wanted to ask the more experienced and know if this is the proper way to do this? If not, could you please tell me what is?
We use a Controller plugin (bootstrapped in application.ini config file) that handles our authentications. It checks the requested controller/action in the preDispatch() phase and matches against ACL objects (could be fetched out of DB, config files, XML, etc.). If the user does not have the privilege to access the target controller/action, the a message is stored in the session and user is redirected to another page, displaying the access forbidden message.
If the user needs to have authentication to access the target controller/action, user is redirected to the login action by modifying the request object.
Using this plugin there is no need to check for user authentication/ACL in each controller and so all the "Access" code would be enclosed in one file, the "Access Plugin".
In order to check for user identity we mostly use the same method of "Zend_Auth::getInstance()->hasIdenity()", but this just shows if the user is authenticated or not. the '''getIdentity()''' method of Zend_Auth returns the current user identity, but again just the identity and not more. However if you would need more information of the user, you could store the user information in a session.
We implement our users as data models, so each user is defined as an object. after a user is authenticated on the login action, we create the appropriate user object and store it in the user session, like this:
// This could be a sample code in AuthController/processloginAction()
// suppose $username is validated before and stores the username
$user = new Default_Model_User($username);
// now $user is our user object, suppose $log is a Zend_Log instance
$log->info("user id '{$user->getId()}' username: '{$user->getUsername()}' logged in");
$sess = Zend_Session_Namespace('auth');
$sess->user = $user;
From now one, the $user property of the session namespace of 'auth' is the user object with all the information you would need about, not just the identity. and whenever you wanted to check if user is logged in (beside using Zend_Auth) you could check for availability of this value on the user session:
$sess = Zend_Session_Namespace('auth');
if (!isset($sess->user) || !$sess->user) {
// user is not logged in, redirect to login page
}
$user = $sess->user;
/*#var $user Default_Model_User*/
$email = $user->getEmail();
now we checked for authentication, and have access to user information (email, phone, etc.).
I use a method similar to the method described by Herman Radtke in his blog at http://www.hermanradtke.com/blog/more-reliable-authentication-in-zend-framework/. Basically create a controller plugin as farzad mentioned:
class My_Authentication extends Zend_Controller_Plugin_Abstract
{
private $_whitelist;
public function __construct()
{
$this->_whitelist = array(
'index/login'
);
}
public function preDispatch(Zend_Controller_Request_Abstract $request)
{
$controller = strtolower($request->getControllerName());
$action = strtolower($request->getActionName());
$route = $controller . '/' . $action;
if (in_array($route, $this->_whitelist)) {
return;
}
$auth = Zend_Auth::getInstance();
if ($auth->hasIdentity()) {
return;
}
self::setDispatched(false);
// handle unauthorized request...
}
}
and then register that plugin in your bootstrap:
public function run() {
$front->registerPlugin(new My_Authentication());
}
I generally take this approach a little farther and integrate the Zend_Acl into the system as well. To do that I would define the plugin below:
class My_Acl_Authentication extends Zend_Controller_Plugin_Abstract
{
private $_acl;
public function __construct($acl)
{
$this->_acl = $acl
}
public function preDispatch(Zend_Controller_Request_Abstract $request)
{
$controller = strtolower($request->getControllerName());
$action = strtolower($request->getActionName());
$route = $controller . '/' . $action;
if (in_array($route, $this->_whitelist)) {
return;
}
$auth = Zend_Auth::getInstance();
$role = 'anonymous';
if ($auth->hasIdentity()) {
$role = $auth->getStorage->read()->role;
}
if ($this->_acl->isAllowed($role, $route)) {
return;
}
self::setDispatched(false);
// handle unauthorized request...
}
}
If you go this route there is some more work to be done, specifically you have to setup the ACL and then you also have to store the user's role in the auth storage.
Thats perfectly ok to do so but to save you from repeating that code you can extend all your controllers from a class A which is subclass of Zend_Controller_Action . Then inside this class declare a method
protected function hasIdentity()
{
return Zend_Auth::getInstance()->hasIdentity();
}
Now in your controller which is subclass of A you can simply do $this->hasIdentity(); instead

How do I get the entity that represents the current user in Symfony2?

I am using the Symfony security setup. Everything works fine, but I don't know how to do one important thing:
In twig, I can reach the current user's info by doing:
Welcome, {{ app.user.username }}
or similar
How do I access this same information in the Controller? Specifically, I want to get the current user entity so I can store it relationally in another entity (one-to-one mapping).
I was really hoping it'd be
$this->get('security.context')->getToken()->getUser()
but that doesn't work. It gives me a class of type
Symfony\Component\Security\Core\User\User
and I want one of type
Acme\AuctionBundle\Entity\User
which is my entity....
Symfony 4+, 2019+ Approach
In symfony 4 (probably 3.3 also, but only real-tested in 4) you can inject the Security service via auto-wiring in the controller like this:
<?php
use Symfony\Component\Security\Core\Security;
class SomeClass
{
/**
* #var Security
*/
private $security;
public function __construct(Security $security)
{
$this->security = $security;
}
public function privatePage() : Response
{
$user = $this->security->getUser(); // null or UserInterface, if logged in
// ... do whatever you want with $user
}
}
Symfony 2- Approach
As #ktolis says, you first have to configure your /app/config/security.yml.
Then with
$user = $this->get('security.token_storage')->getToken()->getUser();
$user->getUsername();
should be enougth!
$user is your User Object! You don't need to query it again.
Find out the way to set up your providers in security.yml from Sf2 Documentation and try again.
Best luck!
Best practice
According to the documentation since Symfony 2.1 simply use this shortcut :
$user = $this->getUser();
The above is still working on Symfony 3.2 and is a shortcut for this :
$user = $this->get('security.token_storage')->getToken()->getUser();
The security.token_storage service was introduced in Symfony 2.6. Prior to Symfony 2.6, you had to use the getToken() method of the security.context service.
Example : And if you want directly the username :
$username = $this->getUser()->getUsername();
If wrong user class type
The user will be an object and the class of that object will depend on your user provider.
The thread is a bit old but i think this could probably save someone's time ...
I ran into the same problem as the original question, that the type is showed as
Symfony\Component\Security\Core\User\User
It eventually turned out that i was logged in using an in memory user
my security.yml looks something like this
security:
providers:
chain_provider:
chain:
providers: [in_memory, fos_userbundle]
fos_userbundle:
id: fos_user.user_manager
in_memory:
memory:
users:
user: { password: userpass, roles: [ 'ROLE_USER' ] }
admin: { password: adminpass, roles: [ 'ROLE_ADMIN', 'ROLE_SONATA_ADMIN' ] }
the in_memory user type is always Symfony\Component\Security\Core\User\User if you want to use your own entity, log in using that provider's user.
Thanks,
hj
In symfony >= 3.2, documentation states that:
An alternative way to get the current user in a controller is to
type-hint the controller argument with UserInterface (and default it
to null if being logged-in is optional):
use Symfony\Component\Security\Core\User\UserInterface\UserInterface;
public function indexAction(UserInterface $user = null)
{
// $user is null when not logged-in or anon.
}
This is only recommended for experienced developers who don't extend
from the Symfony base controller and don't use the ControllerTrait
either. Otherwise, it's recommended to keep using the getUser()
shortcut.
Blog post about it
In Symfony version >= 5 and PHP >= 8.0 you can type hint the authenticated user using Attributes, only in controllers though:
public function indexAction(#[CurrentUser] User $user): Response
{
// do something
}
$this->container->get('security.token_storage')->getToken()->getUser();
Well, first you need to request the username of the user from the session in your controller action like this:
$username=$this->get('security.context')->getToken()->getUser()->getUserName();
then do a query to the db and get your object with regular dql like
$em = $this->get('doctrine.orm.entity_manager');
"SELECT u FROM Acme\AuctionBundle\Entity\User u where u.username=".$username;
$q=$em->createQuery($query);
$user=$q->getResult();
the $user should now hold the user with this username ( you could also use other fields of course)
...but you will have to first configure your /app/config/security.yml configuration to use the appropriate field for your security provider like so:
security:
provider:
example:
entity: {class Acme\AuctionBundle\Entity\User, property: username}
hope this helps!

How do you password protect a page with Wicket?

I want to password protect a webpage in Wicket so the user may only access it if he/she has logged in.
I'd also like the page to show the login page, and then after logging in the original page the user was trying to get to.
How is this done with wicket? I've already created a login page and extended the session class.
The framework-supplied way is to provide an IAuthorizationStrategy instance for your application, e.g., by adding to your Application init() method:
init() {
...
getSecuritySettings().setAuthorizationStrategy(...)
}
A working example of Wickets authorization functionality is on Wicket Stuff here, which demonstrates some reasonably complex stuff. For really simple cases, have a look at the SimplePageAuthorizationStrategy. At a very basic level, this could be used like so (taken from the linked Javadoc):
SimplePageAuthorizationStrategy authorizationStrategy = new SimplePageAuthorizationStrategy(
MySecureWebPage.class, MySignInPage.class)
{
protected boolean isAuthorized()
{
// Authorize access based on user authentication in the session
return (((MySession)Session.get()).isSignedIn());
}
};
getSecuritySettings().setAuthorizationStrategy(authorizationStrategy);
Edit in response to comment
I think the best way forward, if you're just going to use something like SimplePageAuthorizationStrategy rather than that class itself. I did something like this to capture pages that are annotated with a custom annotation:
IAuthorizationStrategy authorizationStrategy = new AbstractPageAuthorizationStrategy()
{
protected boolean isPageAuthorized(java.lang.Class<Page.class> pageClass)
{
if (pageClass.getAnnotation(Protected.class) != null) {
return (((MySession)Session.get()).isSignedIn());
} else {
return true;
}
}
};
Then you'd need to register an IUnauthorizedComponentInstantiationListener similar to what is done in SimplePageAuthorizationStrategy (link is to the source code), which should be something like:
new IUnauthorizedComponentInstantiationListener()
{
public void onUnauthorizedInstantiation(final Component component)
{
if (component instanceof Page)
{
throw new RestartResponseAtInterceptPageException(MySignInPage.class);
}
else
{
throw new UnauthorizedInstantiationException(component.getClass());
}
}
});