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());
}
}
});
Related
I need to display two different index pages to two different user groups. For example, a regular user should see one page, and a privileged user - another one. I see two ways of approaching this issue:
One index action with conditionals:
public function index()
{
// view for privileged users
if(request()->user()->hasRole('privileged')){
return view('index_privileged');
}
// view for regular users
if(request()->user()->hasRole('regular')){
return view('index_regular');
}
return redirect('/');
}
Multiple actions:
public function index_privileged()
{
return view('index_privileged');
}
public function index_regular()
{
return view('index_regular');
}
Which approach is more "restful-friendly" and generally better?
I'm a big fan of light controllers. This might be a little overboard for a simple problem but if something like this pops up again, you'd already have everything all setup.
With that said, it might be best to create a PrivilegedUser class and a RegularUser class and give them both an index method which returns their respective views. Code them both to an interface UserInterface and make sure they both implement that.
Here is what those looked like in my test.
class RegularUser implements UserInterface
{
public function index()
{
return view('index_regular');
}
}
class PrivilegedUser implements UserInterface
{
public function index()
{
return view('index_privileged');
}
}
interface UserInterface
{
public function index();
}
Then you can add a listener which should run for the event Illuminate\Auth\Events\Login. Laravel will fire this event for you automatically when someone logs in. This goes into the file EventServiceProvider.php.
protected $listen = [
'Illuminate\Auth\Events\Login' => [
'App\Listeners\AuthLoginListener',
],
];
Now you can run php artisan event:generate to generate the new listener. Here is what my listener looks like, it should work for you.
namespace App\Listeners;
use Illuminate\Auth\Events\Login;
use Illuminate\Foundation\Application;
class AuthLoginListener
{
/**
* Create the event listener.
*
* #param Application $app
*/
public function __construct(Application $app)
{
$this->app = $app;
}
/**
* Handle the event.
*
* #param Login $event
* #return void
*/
public function handle(Login $event)
{
if ($event->user->hasRole('privileged')) {
$this->app->bind('App\Repositories\UserInterface', 'App\Repositories\PrivilegedUser');
} else if ($event->user->hasRole('regular')) {
$this->app->bind('App\Repositories\UserInterface', 'App\Repositories\RegularUser');
}
}
}
Essentially what this is doing is telling Laravel to load up a certain class based on the type of user that just logged in. The User instance is available through the Login object which was automatically passed in by Laravel.
Now that everything is setup, we barely have to do anything in our controller and if you need to do more things that are different depending on the user, just add them to the RegularUser or PrivilegedUser class. If you get more types of users, simply write a new class for them that implements the interface, add an additional else if to your AuthLoginListener and you should be good to go.
To use this, in your controller, you'd do something like the following...
// Have Laravel make our user class
$userRepository = App::make('App\Repositories\UserInterface');
return $userRepository->index()->with('someData', $data);
Or even better, inject it as a dependency.
use App\Repositories\UserInterface;
class YourController extends Controller
{
public function index(UserInterface $user)
{
return $user->index();
}
}
Edit:
I just realized I forgot the part where you wanted to return redirect('/'); if no condition was met. You could create a new class GuestUser (I know this sounds like an oxymoron) which implements UserInterface but instead of using the AuthLoginListener, I'd bind it in a service provider when Laravel boots. This way Laravel will always have something to return when it needs an implementation of UserInterface in the event it needs this class if no one is logged in.
Well, its more like a refactoring "issue" than a rest-friendly issue. Check this guideline and you can see that most of the things that makes an api friendly is concerned to the url.
But, lets answer what you are asking. The thing you wanna do is a refactoring method but it is not only the move method but something like the extract variable.
The second option would make the code more readable, either ways are right but the second is more developer friendly. It enhances the code readability from any developer. I would recommend using the second option.
Refactoring is never enough, but read something like this, it will help you a lot writing more readable codes.
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.
I'm working on an ACL which is called in Module.php and attached to the bootstrap.
Obviously the ACL restricts access to certain areas of the site, which brings the need for redirects. However, when trying to use the controller plugin for redirects it doesn't work as the plugin appears to require a controller.
What's the best way to redirect outside from outside of a controller? The vanilla header() function is not suitable as I need to use defined routes.
Any help would be great!
Cheers-
In general, you want to short-circuit the dispatch process by returning a response. During route or dispatch you can return a response to stop the usual code flow stop and directly finish the result. In case of an ACL check it is very likely you want to return that response early and redirect to the user's login page.
You either construct the response in the controller or you check the plugin's return value and redirect when it's a response. Notice the second method is like how the PRG plugin works.
An example of the first method:
use Zend\Mvc\Controller\AbstractActionController;
class MyController extends AbstractActionController
{
public function fooAction()
{
if (!$this->aclAllowsAccess()) {
// Use redirect plugin to redirect
return $this->redirect('user/login');
}
// normal code flow
}
}
An example like the PRG plugin works:
use Zend\Mvc\Controller\AbstractActionController;
use Zend\Http\Response;
class MyController extends AbstractActionController
{
public function fooAction()
{
$result = $this->aclCheck();
if ($result instanceof Response) {
// Use return value to short-circuit
return $result
}
// normal code flow
}
}
The plugin could then look like this (in the second case):
use Zend\Mvc\Controller\Plugin\AbstractPlugin;
class AclCheck extends AbstractPlugin
{
public function __invoke()
{
// Check the ACL
if (false === $result) {
$controller = $this->getController();
$redirector = $controller->getPluginManager()->get('Redirect');
$response = $redirector->toRoute('user/login');
return $response;
}
}
}
In your question you say:
[...] it doesn't work as the plugin appears to require a controller.
This can be a problem inside the controller plugin when you want to do $this->getController() in the plugin. You either must extend Zend\Mvc\Controller\Plugin\AbstractPlugin or implement Zend\Mvc\Controller\Plugin\PluginInterface to make sure your ACL plugin is injected with the controller.
If you do not want this, there is an alternative you directly return a response you create yourself. It is a bit less flexible and you create a response object while there is already a response object (causing possible conflicts with both responses), but the plugin code would change like this:
use Zend\Mvc\Controller\Plugin\AbstractPlugin;
use Zend\Http\PhpEnvironment\Response;
class AclCheck extends AbstractPlugin
{
public function __invoke()
{
// Check the ACL
if (false === $result) {
$response = new Response;
$response->setStatusCode(302);
$response->getHeaders()
->addHeaderLine('Location', '/user/login');
return $response;
}
}
}
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.
I'm creating a mvc3 canvas app using facebook c# sdk
The method name is create.
I also do a post and have another create method with [HttpPost] attribute.
When I add the [CanvasAuthorize(Permissions = ExtendedPermissions)] attribute to both the create methods, and a link from another page calls this create method, normally the get method should get called but in this case the post method gets called
But if I comment the post method then it goes to the get method.
Any ideas how to solve this.
Thanks
Arnab
This is because of the canvas authorization posting the access token into the page. The only way around it I've found is to create a different action that deals the post and use that action inside the view as post target. It will look something like this:
// /MyController/MyAction
// Post and Get
[CanvasAuthorize(Permissions = ExtendedPermissions]
public ActionResult MyAction(MyModel data)
{
MyModel modelData = data;
if(data==null)
{
modelData = new MyModel();
}
else
{
modelData = data;
}
return View(modelData);
}
// /MyController/MyActionPost
// POST only
[HttpPost]
[CanvasAuthorize(Permissions = ExtendedPermissions]
public ActionResult MyActionPost(MyModel data)
{
if(Model.IsValid)
{
//Processing code with a redirect at the end (most likely)
}
else
{
return View("MyAction", data);
}
}
Then in your MyAction view:
#using (Html.BeginForm("MyActionPost", "MyController"))
{
<!-- Form items go here-->
<inpuy type="submit" value="Submit" />
#Html.FacebookSignedRequest()
}
I have the same issue. It was doing a GET before, then suddenly when browse to an action with [CanvasAuthorize(Permissions = ExtendedPermissions)] attribute, it's doing a POST instead of a GET.