Now: resolved - no reproducible anymore
For some specific application security, I have the following createQuery function on a table, ie you can only access the table record if you have the "Administrator" credential or if you are the user that is stored in the MembershipDelegate relation.
class OrganisationTable extends Doctrine_Table
function createQuery($alias = ''){
if (!$alias){
$alias = 'o';
}
$query = parent::createQuery($alias);
try {
$user = sfContext::getInstance()->getUser();
}catch(Exception $e){
if ($e->getMessage() == 'The "default" context does not exist.'){
return $query;
}else{
throw $e;
}
}
if ($user->hasCredential('Administrator')){
//all good
}else{
$userId = $user->getAttribute('userId');
print "<!--testaa ".print_r($user->getCredentials(),1).'-->';
$query->
leftJoin("$alias.MembershipDelegate mdelsec")->
addWhere ("mdelsec.joomla_user_id=$userId");
}
return $query;
}
This seems to work fine at all levels, however there is a choice validator for which the $user object seems to come back empty
/**
* Person form base class.
*
*/
...
abstract class BasePersonForm extends BaseFormDoctrine
{
public function setup()
{
$this->setWidgets(array(
...
'organisation_id' => new sfWidgetFormDoctrineChoice(array('model' => $this->getRelatedModelName('Organisation'), 'add_empty' => true)),
class PersonForm extends BasePersonForm{
public function configure(){
$this->widgetSchema['organisation_id']->setOption('renderer_class', 'sfWidgetFormDoctrineJQueryAutocompleter');
$this->widgetSchema['organisation_id']->setOption('renderer_options', array(
'model' => 'Organisation',
'url' => NZGBCTools::makeUriJoomlaCompatible(
sfContext::getInstance()->getController()->genUrl('organisation/jsonList'))
));
$this->validatorSchema['organisation_id']->setOption('required',false);
is there any other way to get the user object in the model?
This approach to row level security may not be MVC by-the-book, but is IMO safer and superior than implementing the same security concepts in the actions:
It can be used with out of the box admin-generator modules
It is much harder to forget to implement it somewhere
It may at times not require any credentials, only Super Admin access
I'm not aware of another way to get the session user in the models (I don't think there is one), but if you're able to, you ought to pass the user object down to the models.
I think you are missing something here, you have mixed the MVC layers quite a lot. My suggestion is to make the Model independent from the Controller (delegate to the Controller the Context-specific situations), and validation should be done by the Controller also ( there should be an action that receives the request , binds the form and validates it. There is where you have to definy anithing thats context speficif). Here is what i would do:
The OrganisationTable class should have 2 methods: createQueryForAdministrator , and createQueryForMembershipDelegate . Each one does the things it should do, and now you should change the Controller (the action that handles it) and do something like:
public function executeAction(sfWebRequest $request)
{
if ($this->getUser()->hasCredential('administrator'))
{
//call the administrator method
} else {
//call the other method
}
}
The action that instances the Form should also check the user credentials and do something like:
If ($user->hasCredential("administrator"))
{
sfContext::getInstance()->getConfiguration()->loadHelper("Url");
$form->getValidator("organization_id")->setOption("url",url_for("#action"));
[..]
} else {
[..]
}
Check the url helper reference, you can do thinks like loading helpers on actions etc and you could also create your own helpers too. Hope this helps!
EDITED: Check this post about sfContext class and alternatives
It now appears that this question was a fluke as I can't reproduce the problem after fixing other issues around the user authentication.
Related
I've written a custom claims authorizatize attribute and I would like to unit test the code I've written but have been unable to find what I'm looking for on SO.
For example, this is my custom authorize attribute class:
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using System.Web;
using System.Web.Mvc;
using System.Web.Security;
using IPD2.MVC.Interfaces.Providers;
using IPD2.MVC.Providers;
namespace IPD2.MVC.Attribute
{
public class ClaimsAuthorizeAttribute : AuthorizeAttribute
{
private readonly string _claimValue;
private readonly string _claimType;
private readonly ITokenProvider _tokenProvider;
public ClaimsAuthorizeAttribute(string type, string value)
{
_claimType = type;
_claimValue = value;
_tokenProvider = new TokenProvider();
}
public override void OnAuthorization(AuthorizationContext filterContext)
{
var jwt = _tokenProvider.ApiToken as JwtSecurityToken;
if (jwt == null)
{
HandleUnauthorizedRequest(filterContext);
}
else
{
var claim = jwt.Claims.FirstOrDefault(expr => expr.Value == _claimValue);
var authCookie = HttpContext.Current.Request.Cookies[FormsAuthentication.FormsCookieName];
if (authCookie != null)
{
var formsAuthenticationTicket = FormsAuthentication.Decrypt(authCookie.Value);
if (formsAuthenticationTicket != null && !formsAuthenticationTicket.Expired)
{
var roles = formsAuthenticationTicket.UserData.Split(',');
HttpContext.Current.User = new System.Security.Principal.GenericPrincipal(new FormsIdentity(formsAuthenticationTicket), roles);
}
}
if (claim != null)
{
base.OnAuthorization(filterContext);
}
else
{
HandleUnauthorizedRequest(filterContext);
}
}
}
}
}
Test code
public class GivenCallingClaimAuthorizationAttribute : SpecsFor<ClaimsAuthorizeAttribute>
{
//HttpActionContext actionContext;
//IPrincipal originalPrincipal;
protected override void Given()
{
SUT = new ClaimsAuthorizeAttribute(ClaimTypes.Role, "ImNotAllowedToUseController :(");
}
public class WhenUserIsNotAllowedToAccessController : GivenCallingClaimAuthorizationAttribute
{
protected override void When()
{
SUT.OnAuthorization(
new AuthorizationContext()
);
}
}
[Test]
public void ThenAssertSomethingBasedOnCodeInTest()
{
//todo: some assert
}
}
I've written this basic test class with the SpecsFor BDD framework but I'm not sure what it needs to test it succesfully.
Any ideas on how to test this? As you can see, I'm testing the class itself rather than a controller with the attribute. I'm not sure about a good way to test this.
If you want to do BDD, think about the behaviour of your class. What kind of things should it be able to do?
For instance, perhaps it should:
- authorize current users
- filter users whose ticket has expired
- assign authorized users with the correct roles
- etc.
Let's say we're going to look at the second one of those, because it's interesting. I'll ask you, "Can you give me an example of when a ticket might expire?"
And you say, "Yes, the timeout is 500ms so anything older than that is expired." Or you say, "These are tickets to a football match and are given out yearly." (I don't know what a ticket is, but this conversation would help me work that out.)
So then we can write an example:
Given we've got a handler for unauthorized tickets
Given Fred's ticket expired on 2017-05-14
And it's now 2017-05-14
When we try to authenticate Fred
Then Fred should not be authenticated
And the handler should be given Fred's details
Next I like to put these in comments in code, with the "should" as the title of the test. After that, it's just a matter of setting up the context (in the Given), passing it to your code to exercise (the When) and verifying the outcome (the Then).
However, you can see that your Specs framework only allows for one given! That's not great, since I've already identified several scenarios which require different contexts. If you use SpecsFor the way you're using it, you'll have to make one class for each of my scenarios, and they won't be very easy to read!
I highly recommend using the second style instead, and start just with the Given, When, Then in comments.
The thing is, unit tests' steps aren't really reused much, compared to say a full-system BDD scenario, in which contexts are often called several times for different capabilities. A unit of code (or class) tends to fulfil one responsibility, or it gets refactored that way, and its contexts don't bleed into other classes because Mocks.
So, I find that just having Given, When and Then in comments is enough.
I therefore recommend you switch to the "old school" way of doing things that I can see in this page (down the bottom) and just start with the comments. See how that goes for you.
By the way, if you really want to do BDD properly, then you want to drive the development with the examples of how it behaves, and incrementally fill in the code to make those examples work, rather than writing them down. The book "Growing Object Oriented Software, Guided By Tests" will be very helpful for you.
This appears to go against the "convention" nature of Grails. If I don't explicitly call the render() method with my view, then the service injection in my Interceptor is null.
Interceptor:
class GlobalParamsInterceptor {
def SysConfigService sysConfigService;
GlobalParamsInterceptor() {
matchAll();
}
boolean before() {
true
}
boolean after() {
model.isApplicationOpen = sysConfigService.isApplicationOpen();
true
}
void afterView() {
// no-op
}
}
Controller:
class ConfigController {
static namespace = "coordinator";
def index() {
render(view: "index");
}
}
If I comment out render(view: "index") in my controller then sysConfigService in my Interceptor is null, otherwise it works great. Can someone please explain why that is?
Edit:
Grails 3.1.3
Edit #2
It appears i was mistaken, the sysConfigService is not null...its the model that is null so I am unable to set the isApplicationOpen property on it. Just the same though, I don't believe I should have to call the render() method in order to access the model as I thought that was an inherited property from the framework.
If I comment out render(view: "index") in my controller then
sysConfigService in my Interceptor is null, otherwise it works great.
I don't think think that is possible. The dependency injection into your interceptor happens and is completed before any requests can ever enter the application. Invoking render (or anything else) in a controller action can't affect that.
Also, you have def SysConfigService sysConfigService. The def there is superfluous. You can delete it and the compiler will generate the same code. def is only sensible there if you remove SysConfigService.
EDIT:
I realized after posting that that I did not address the question in the title:
Is render() is required when using Interceptors?
The answer is "no".
In your example though, your calling render from your controller. My
issue is that If I don't call render and rely on the "convention" of
Grails to auto detect my "index.gsp" instead, that is when model in my
interceptor is NULL.
See the commit at https://github.com/jeffbrown/raymond/commit/f354047158038f571630ab7e3e0416850bdde8a8.
When I send a request to /demo/report I see the following output on stdout:
Model: [name:Raymond]
That output is coming from https://github.com/jeffbrown/raymond/blob/f354047158038f571630ab7e3e0416850bdde8a8/grails-app/controllers/demo/FooInterceptor.groovy#L13.
I have created a Project to display adverts. Each Advert has a Location.
Currently a list if adverts are displayed. I would now like a list of locations in my layout.phtml and after a click on the location the adverts should be filtered.
To achieve this I have created a new Module called Geolocation. I have then created two new view helpers; one to show all the Locations and the other is to display the name of the chosen Location, which is stored in a Cookie.
When you click on a location in the list you access a AJAX request is made to the Geolocation Controller. The controller calls a method in a service to store the location in the cookie.
I am now changing my SQL Queries and Repositories in my Advert Module to accept location if it is set:
public function countAdvertsByCategory($location=false)
Normally, I would add $location = $_COOKIE['ChosenCounty'] in my advert controller, but I am sure there is a better way.
I would have thought that I can maybe add this in the module.php from the Geolocation Module. If that Module has included the variable, $location will be set with the cookie value and otherwise it will just be ignored.
Is that the right way or what is the best practice? And how would I do that?
UPDATE
I have now changed my Factory:
namespace Application\Navigation;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
class MyNavigationFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $serviceLocator)
{
// previous without Geolocation
$navigation = new MyNavigation();
return $navigation->createService($serviceLocator);
$location = $serviceLocator->get('Geolocation\Service\Geolocation');
$navigation = new MyNavigation($location);
return $navigation->createService($serviceLocator);
}
BUT, if I would now remove my Geolocation Module, than the Factory in my Application Module to create my Navigation would fail, meaning my Factory is now dependent of this new Module which I did not want. How could I avoid this?
You could add the cookie value as a 'service' to the service manager. Whenever you need the $location you would then just retrieve it from the service manager.
Create a factory that accesses the required cookie variable.
namespace GeoLocation\Service;
use Zend\ServiceManager\ServiceLocatorInterface;
use Zend\ServiceManager\FactoryInterface;
class GeoLocationFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $serviceLocator)
{
$request = $serviceLocator->get('Request');
$cookies = $request->getHeaders()->get('cookie');
return isset($cookies->location) ? $cookies->location : false;
}
}
Then register it with the service manager in module.config.php.
'service_manager' => [
'factories' => [
'GeoLocation\Service\GeoLocation' => 'GeoLocation\Service\GeoLocationFactory',
],
],
Then you can update your AdvertService to require the value
class AdvertService
{
protected $location;
public function __construct($location)
{
$this->location = $location;
}
public function getAdvertsByCategory()
{
return $this->repository->countAdvertsByCategory($this->location);
}
}
You can then create a new AdvertServiceFactory that will fetch and inject the service into the AdvertService::__construct using
$serviceManager->get('GeoLocation\Service\GeoLocation');
i have an best practice question.
Its clear what Post/Redirect/Get does, but what will be the bestpractice to handle them?
I think there are 2 methods to handle them.
1.) We call the prg plugin at first on controller action
2.) We first validate the post data, and only redirect to the prg-response if successfull?
My problem about this is, at
1.) We enlarge the response time because of the redirect, this is by default so i think not the best solution
2.) will create an overhead by the every time validation of the form
What did you mean is the better solution aber this case?
regards
UPDATE:
What i mean is, the normal(standard) case is something like this - http://framework.zend.com/manual/2.0/en/modules/zend.mvc.plugins.html#the-post-redirect-get-plugin.
$prg = $this->prg('url');
if ($prg instanceof Response) {
return $prg;
} elseif ($prg === false) {
return new ViewModel(array(...));
}
$form->setData($prg);
This means, that theres after every form submit an redirect executes.
Now, my idea was something like this:
$prg = $this->prg();
$form = $this->getFormLogin();
$data = ($prg instanceof ResponseInterface)
? $this->getRequest()->getPost()
: $prg;
if (false !== $data) {
$form->setData($data);
if (true === $form->isValid()) {
if ($prg instanceOf ResponseInterface) {
return $prg;
}
// Make something within the loginservice or something else
}
The idea behind this was, to only redirect for the PRG only if the form is valid, to save response time and other things (because of bootstrapping settings etc.)
The Zend Framework is designed based on Front-Controller pattern so its essential to redirect the page when you access different resources(controller-action).
moreover when you fire redirect(URL) function from your source code it takes minimal time when you compared the time to access the same(URL) from your browser.
You could reduce the response time to considerable amount when you use classmap_autoloading.
Updated:
for an example i take login process, in the below code i implement both HTTP get and post methods in the same action() but, you can refactor this function based on HTTP methods.
LoginController.php
public function loginAction()
{
//code
if ($request->isPost()) {
//code
if ($isValid) {
return $this->redirect()->toUrl('urUrl');
}
return $this->redirect()->toUrl('urUrl');
}
//code
return $viewModel;
}
After refactoring above code
//it used for HTTP get
public function loginAction()
{
//code
return $viewModel;
}
//it used for HTTP POST
public function loginPostAction()
{
//code
if ($notValid) {
return $this->redirect()->toUrl('urUrl');
}
$viewModel->setTemplate($viewpath);
return $viewModel;
}
You need to modify your routing configuration in such a way to handle for both HTTP get and post methods. if the request is HTTP-get the controller process the loginAction() but if its HTTP-post it process the loginPostAction()
Zend framework 2 - HTTP method Routing
Updated:
The purpose of plugin is to avoid the user to POST the data again to the browser. In your case you are trying to enable the option to POST their data when the form is not valid (you are trying to change the behaviour of PRG plugin). if you really worried about response time don't use PRG plugin. create your custom logic inside your controller-action.
--SJ
I'm creating a dynamic Application in which the content is added through a CMS. Inside the CMS, I'm setting a db entry which states what module to use for each content page.
NodeId,
ParentNodeId,
Name_de,
Name_en,
ModuleName,
foreignkey_ContentLinks,
in this table entries look as follows:
6,
1,
Veranstaltung-21-02-2013,
Event-21-02-2013,
Events,
682
The entire tree should end up in my navigation (and perfectly also in my routing). I do not want to add it in some controller, because my Application consists of a whole bunch of Modules and I want to access that Info across all my Modules.
I already tried injecting it in the global.php, but to no avail because I can't my db adapter or any other important classes at that stage.
Any ideas or links to best practices?
The navigation containers are composed by factory classes. The easiest approach is to write your own factory and have the getPages() method fetch pages from a database instead of from config. If you extend from the AbstractNavigationFactory you only need to write a couple of methods.
<?php
namespace Application\Navigation\Service;
use Zend\Navigation\Service\AbstractNavigationFactory;
use Zend\ServiceManager\ServiceLocatorInterface;
class CmsNavigationFactory extends AbstractNavigationFactory
{
/**
* #param ServiceLocatorInterface $serviceLocator
* #return array
* #throws \Zend\Navigation\Exception\InvalidArgumentException
*/
protected function getPages(ServiceLocatorInterface $serviceLocator)
{
if (null === $this->pages) {
$application = $serviceLocator->get('Application');
$routeMatch = $application->getMvcEvent()->getRouteMatch();
$router = $application->getMvcEvent()->getRouter();
// get your pages from wherever...
$pages = $this->getPagesFromDB();
$this->pages = $this->injectComponents($pages, $routeMatch, $router);
}
return $this->pages;
}
public function getName()
{
// this isn't used if fetching from db, it's just here to keep the abstract factory happy
return 'cms';
}
}
Add the factory to the service manager, just like you would for other containers
'service_manager' => array(
'factories' => array(
'CmsNavigation' => 'Application\Navigation\Service\CmsNavigationFactory',
),
),
And use it with the navigation view helpers in the same way
<?php echo $this->navigation()->menu('CmsNavigation'); ?>
Responding to your comment on #Crisp's answer, and for future googlers, I'll explain how to do something similar for routing.
Typically you would want to create a custom router that can match URLs to the pages in your database, similarly to the standard Segment router. To do this, you will have to implement the Zend\Mvc\Router\RouteInterface interface. For example:
namespace Application\Router;
use Zend\Mvc\Router\RouteInterface;
use Application\Model\CMSTable;
class CmsRoute implements RouteInterface, ServiceLocatorAwareInterface
{
protected $table;
// Service locator injection code
public function getCmsTable()
{
// Retrieve the table from the service manager
}
public function match(Request $request)
{
// Match the request on some route field, etc.
}
public function assemble(array $params = array(), array $options = array())
{
// Assemble a URL based on the given parameters (e.g. page ID).
}
public static function factory($options = array())
{
// Construct a new route, based on the options.
}
}
You could then register this route as an invokable for the RoutePluginManager in your module configuration:
'route_manager' => array(
'invokables' => array(
'Cms' => 'Application\Router\CmsRoute'
),
),
Then, you can create a new route (just as you would for any other route) with type Cms. The route plugin manager will create your route instance, and since CmsRoute implements ServiceLocatorAwareInterface, the plugin manager will inject itself in the route. In turn, the plugin manager has the main service manager set, so that you can get the database table from there!
Of course you can match on page ID, but if you have a hierarchical structure, it's nicer to reflect that in your URLs. I would therefore recommend adding a route field to the database schema and match on that, beginning with the tree root and working down.