I'm learning how to use Slim Framework and I have a problem using flash message inside a custom middleware.
The problem is quite simple: I use the $app->flash('error', 'my message'); in the middleware and there is no message in the next page.
The middleware works great by the way
Here is my code:
Middleware
class SessionMiddleware extends \Slim\Middleware {
public function call() {
$app = $this->app;
$req = $app->request;
if(!preg_match('#^(/public)#', $req->getPath())) {
if(isset($_SESSION['user']) && !empty($_SESSION['user'])) {
//Do something
} else {
$app->flash('error', 'You must be logged');
if($req->getPath() != '/login') {
$app->redirect('/login');
}
}
}
$this->next->call();
}
}
Login
<?php
if(isset($_SESSION['slim.flash']['error'])) {
echo '<p class="alert alert-danger"><strong>Error</strong>: '.$_SESSION['slim.flash']['error'].'</p>';
}
?>
App
$app = new \Slim\Slim(array(
'mode' => 'development',
'debug' => true,
'templates.path' => './templates'
));
$app->add(new \SessionMiddleware());
$app->get('/login', function() use($app) {
$app->render('login.php');
});
Any ideas how to fix that ?
Thank you
Try to use flashNow instead of flash method.
I also saw that 'flash' and 'flashNow' methods are not working in middleware. To add flash message, I decided to do it manually. It's working, but I know that's not the best approach.
$_SESSION['slim.flash']['error'] = 'message';
Running into this issue myself.
Since Flash is an app middleware component and is added by default, and before you add any custom app middleware components, it won't actually be initialised when your middleware is called.
Doing what #kkochanski has done is hacky, but is probably the only option, short of removing/unsetting Flash and adding it as the final app middleware component.
I'm facing with the same problem and resolve it. You can achieve that with call another method in authenticated route and flashing inside it for example
class AuthController
{
public function flashError($request, $response)
{
// flash
// redirect
}
}
It works well for me. So you can just redirect to and make sure this method handle it. So you can flashing message outside middleware.
Related
I want to protect my routes with authorization levels in laravel - client users have access to one group while admin gets access to a separate group. However, the laravel API I'm creating runs concurrent to the existing legacy app which uses its own Users class, rather than the pre rolled eloquent Users model that all the docs use.
So far I haven't been able to determine how best to create this custom authorization middleware.
Basically I'd like to do something like this:
Route::group(['middleware' => 'custom_auth', function() {
Route::get('/' function() {
return "Hello World";
}
}];
//where 'custom_auth' points to something like
function isAdmin() {
if (Core\User->check_is_admin()) {
return true;
} else {
return false;
}
}
Kind of an open-ended question, I know, so any links to blogs/docs/videos would be appreciated. Thanks!
Solved:
I added custom middleware to App\Http\Middleware that copied the structure of the prebuilt Middleware\Authenticate.php file and uses the custom isAdmin() method.
Make sure to include in HTTP\Kernel.php
protected $routeMiddleware = [
'auth' => \App\Http\Middleware\Authenticate::class,
'auth_admin' => \App\Http\Middleware\AuthenticateAdmin::class, // <- New Middleware
...]
Then in my Routes\admin_api
Route::group(['middleware' => 'auth_admin'], function () {
Route::get('/', function () {
return "Hello World";
});
This all works!
I have some code in app\ExceptionsHandler.php that checks if a URL exists, if not then it will check the database if it is a custom URL, if so then it will return a view. The problem with this is, I think, because it is not being passed through the web middleware, none of the session and auth is being started because Auth::id() is null, even though I am 100% logged in
Is there an easier way to do this?
public function render($request, Exception $exception)
{
if($exception instanceof NotFoundHttpException)
{
if(\App\EventMeter::isURL()) {
$ec = new EventsController();
return $ec->eventMeter();
}
if(\App\Tournament::isTournamentURL()) {
$t = new TournamentController();
return $t->home();
}
if(BountyHunter::isURL())
{
$bhc = new BountyHunterController();
return $bhc->home();
}
return response()->view('errors.missing',array(), 404);
}
return parent::render($request, $exception);
}
Maybe it's something that you should shift the responsibility to your routing.
Route::get('{uri}', ['uses' => 'PageController#view', 'as' => 'page'])
->where('uri', '.*');
A Controller can handle all your views and can perform the routing and redirect, allowing you to have any other Route that you need. By creating a class, for example, a LayoutService that can handle all your different Events, Tournament, Bounty which can redirect to an appropriate location. This will let you have access to all your web middleware.
// Logic to determine what method you want to be called
$template = 'events|tournament|etc';
if (method_exists($this->layoutService, $template)
&& is_callable(array($this->layoutService, $template))) {
return call_user_func(
array($this->layoutService, $template)
);
}
Lets say that my page has 10 sections, in 6 of them I have to check if the user is logged in, and if not, redirect him to the "login/register" page.
I found myself repeating this code in the controller of those 6 pages:
public function actionthatneedsauthAction()
{
$sl = $this->getServiceLocator();
$authService = $sl->get('doctrine.authenticationservice.orm_default');
$user = $authService->getStorage()->read(); //is the user logged in?
if ($user) { //auth successful
//-------------/*CODE FOR THIS SPECIFIC CONTROLLER GOES HERE*/--------
return new ViewModel(array(
'user' => $user,
'somethingelse' => $somethingelse
));
} else { //auth denied
return $this->redirect()->toRoute(user, array('action' => 'login'));
}
}
I tried to encapsulate that into a service, called islogged (this is a model, not a controller), but I couldn't make it work because I couldn't find a way to redirect to a controller from inside a model, I only know how to redirect to a controller via another controller.
So in my usermanager.php I had a function like this one:
public function islogged()
{
$sl = $this->getServiceLocator();
$authService = $sl->get('doctrine.authenticationservice.orm_default');
$user = $authService->getStorage()->read(); //is the user logged in?
if ($user) { //auth successful
return $user;
} else {
/*
redirect to the login screen, dont know how to do it,
this code doesnt work here:
return $this->redirect()->toRoute(NULL, array(
'controller' => 'user',
'action' => 'login'
));
*/
}
}
so the idea was that in my controllers I only had to write:
$user = islogged();
and all the code repetition I mentioned won't be necessary anymore.
Is it a good practice what I tried to do with the usermanager.php islogged function?
If it is a good practice, how am I supposed to redirect to a controller from inside a model?
If is not a good practice, which will be the way to avoid all that code repetition that I'm having in my controllers?
I know that I can put the authentication step into the onboostrap() but in that case the auth will be triggered for all of my pages, and I just want it in some of them.
I would advise you to implement Doctrine Authentication with official DoctrineModule Authentication described in the docs folder of the repo.
Read this - Link to DoctrineModule Authentication
Then you can handle your Authentication check via the zf2 own Controller and View Helpers identity. See example in the docs here.
I use this ACL Module on my apps: https://github.com/ZF-Commons/zfc-rbac
My Customer overview controller then looks as so:
<?php
namespace RoleBasedCustomer\Controller;
use RoleBasedUser\Service\AuthenticationService;
use RoleBasedUser\Service\UserService;
use RoleBasedUser\Controller\AbstractMultiModelController;
class OverviewController extends AbstractMultiModelController
{
public function __construct(
AuthenticationService $authService,
UserService $userService
) {
$this->authService = $authService;
$this->userService = $userService;
}
public function indexAction()
{
if ( ! $this->authService->hasIdentity() ) {
return $this->redirect()->toRoute('customer/login');
}
}
}
The only thing i had to do is replace these two lines:
$authService = $this->getServiceLocator()
->get('doctrine.authenticationservice.orm_default');
$user = $authService->getStorage()->read(); //is the user logged in?
with this one:
$user = $this->identity();
I'm building an API for a webapp I made to train myself in Laravel. I integrated a token based authentication system by Tappleby, like this:
Route::get('api/v1/auth', 'Tappleby\AuthToken\AuthTokenController#index');
Route::post('api/v1/auth', 'Tappleby\AuthToken\AuthTokenController#store');
Route::delete('api/v1/auth', 'Tappleby\AuthToken\AuthTokenController#destroy');
Route::group(['prefix' => 'api/v1', 'before' => 'auth.token'], function ()
{
Route::resource('user', 'ApiUsersController');
});
In ApiUsersController I, ideally, want to do something like this:
public function index()
{
$payload = $request->header('X-Auth-Token');
if(empty($payload)) {
return $this->respondNotFound('User does not exist.');
}
$user = $this->driver->validate($payload);
return $user;
}
However, header() is not available for the controller. How can I solve this?
In Laravel, you can retrieve the HTTP headers like so:
$value = Request::header('Content-Type');
Add this to your controller and you can then do what you need to with it.
Also, you can change Content-Type to whatever it should be.
Read more here: http://laravel.com/docs/4.2/requests
I have two controllers, homepage and Security.
In the homepage, I am displaying one view and in the security, I am doing some things, and one of them is the email address validation.
What I would like is that when the email validation code is not valid, display the homepage with a flash message. For that, I will have to render the indexAction of the HomepageController, from the Security controller, by giving him as parameter the flash message.
How can this be done? Can I render a route or an action from another controleller?
Thank you in advance.
I believe the checking should not be done in the Security controller. Right place in my opinion is a separate validator service or right in the entity which uses the email address.
But to your question, you can call another controller's action with $this->forward() method:
public function indexAction($name)
{
$response = $this->forward('AcmeHelloBundle:Hello:fancy', array(
'name' => $name,
'color' => 'green',
));
return $response;
}
The sample comes from symfony2 documentation on: http://symfony.com/doc/2.0/book/controller.html#forwarding
I have found the solution, simply use the forward function by specifying the controller and the action nanme:
return $this->forward('MerrinMainBundle:Homepage:Index', array('flash_message'=>$flash_message));
redirectToRoute : Just a recap with current symfony versions (as of 2016/11/25 with v2.3+)
public function genericAction(Request $request)
{
if ($this->evalSomething())
{
$request->getSession()->getFlashBag()
->add('warning', 'some.flash.message');
$response = $this->redirectToRoute('app_index', [
'flash_message' => $request->getSession()->getFlashBag(),
]);
} else {
//... other logic
}
return $response;
}