I’m new to slim and trying to figure out the best way to create a global user variable. My first thought is to add it to the Container, but I can’t figure out how to inject $app into a route mapped to a class method. I saw somewhere that I can add Container $container to the constructor and the DI should do it automatically? Doesn’t seem to be working.
__construct(Container $container)
Plus, I need to access the variable in a middleware class I wrote to parse my Bearer header, and I can’t find any details on how to do that. I know I can pass $app into each ->add, but that’s a lot of repeated code, and I’m hoping I can avoid that. I'm already injecting an extra variable to parse for roles:
->add(new \App\Middleware\AuthMiddleware('admin'));
public function __construct($role = null)
I can always create a PHP global, but I'd like to figure out what the right Slim way of doing this is.
You can do this with a CurrentUser-class which stores the roles:
class CurrentUser {
private $roles;
function getRoles() {return $this->roles; }
function setRoles($newRoles) {$this->roles = $newRoles; }
}
Which then you can add to the container:
$container[CurrentUser::class] = function($c) {
$user = new CurrentUser();
$user->setRoles(['myrole']);
return $user;
};
And use the CurrentUser-object in a helper method which dynamically creates us the wanted middleware. This method could also be on the CurrentUser-class
function hasPermission($role) {
return function($req, $resp, $next) use ($role) {
if(in_array($role, $this[CurrentUser::class]->getRoles())) {
return $next($res, $resp); // proceed to route
} else {
// handle unauthorized
return $resp->withStatus(401)->write('unauthorized');
}
};
}
Now use the helper method to create the middleware which authorize the user.
$app->get('/books', function ($request, $response, $args) {
return $response->write('Some books yay');
})->add(hasPermission('myrole'));
Related
When I extends ApiBaseController in another class, response token denied is doesn't work. even though I put wrong app-token but still give response in another class.
class ApiBaseController extends Controller
{
protected $user;
public function __construct()
{
if (request()->header('app-token') != 'ofdsafalkhguddskjafl01JhBF9mGx2jay'){
return response()->json([
'success'=>false,
'status'=>'401',
'message'=>'Token Denied !',
'response'=>[
'total'=>0,
'data'=>[]
]
]);
}
else{
$this->user = Auth::guard('api')->user();
}
}
}
This class still work even though I put wrong app-token
class AttendeesApiController extends ApiBaseController
{
public function index(Request $request)
{
return Attendee::scope($this->account_id)->paginate($request->get('per_page', 25));
}
}
I want to make sure when app-token is wrong will give Token Denied ! response
please give me some advice
You will have to call parent constructor to make this work.
class AttendeesApiController extends ApiBaseController{
function __construct(){
parent::__construct();
}
public function index(Request $request){
return Attendee::scope($this->account_id)->paginate($request->get('per_page', 25));
}
}
If I am not mistaken, you will also have to put a kind of a die in the constructor to avoid further execution.
Update:
Best way to handle this is to group these routes inside a middleware and have the bearer token check in the middleware itself. This will make your approach more neat and you can easily add new routes that require bearer token check in this route middleware group.
While it is a good idea to keep the token validation concern separated, it is not a good practice to do such thing in the constructor, let alone hide it in the constructor of a base class.
In general, constructors should be used to construct the object, not to "do things".
Because you want to return early, it gets a bit complicated to extract this concern out of the controller. But that's what middleware is for.
Take a look at the Laravel documentation on creating your own middleware (altough what you are trying to do might be already built in)
An example middleware class could look like this:
<?php
namespace App\Http\Middleware;
use Closure;
class CheckToken
{
/**
* Handle an incoming request and check the token.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if (...) { //your token check
return ...; // your early-returned json.
}
return $next($request); //otherwise continue
}
}
I'm attempting to rebind what $request->user() returns, and having poked through the built in authentication code, I found a service using app->rebinding to request->setUserResolver is how it's done? I tried it myself, with no luck. I created a service (well, coopted AuthServiceProvider, and changed the register to:
public function register()
{
$this->app->rebinding('request', function ($app, $request) {
$request->setUserResolver(function () use ($app) {
$token = $this->request->bearerToken();
dd($token);
// error_log($token);
return array('user' => 1);
});
});
}
Ignoring the dd, which is there to test, how can I find where I'm going wrong? I even found a SO answer that seems to indicate this is the way to go but nothing gets dumped, nothing gets logged (when error log isn't commented out) and dumping $request->user() in my controller just returns null.
I know I can use the built in auth/guard setup, but I figured since I'm not using most of what the auth/guard setup has, why not try to learn and set it up myself? Of course, so far I've gotten nowhere. I'm going to fall back to using the built-in stuff, but I'd like to learn and improve.
As I realized it may make a difference, I'm running Lumen 5.4.
In Lumen, your App\Providers\AuthServiceProvider class comes by default with
public function boot()
{
// Here you may define how you wish users to be authenticated for your Lumen
// application. The callback which receives the incoming request instance
// should return either a User instance or null. You're free to obtain
// the User instance via an API token or any other method necessary.
$this->app['auth']->viaRequest('api', function ($request) {
if ($request->input('api_token')) {
return User::where('api_token', $request->input('api_token'))->first();
}
});
}
This is the place to define the user resolution logic. The rebinding you were registering in the register method was being supeseded by this one.
Just uncomment the $app->register(App\Providers\AuthServiceProvider::class); line in bootstrap/app.php to register your provider; don't modify the code in the vendor folder (if I understood correctly you were doing that).
Update
I now see what you mean, although I'm not sure it is really too much "load" for the auth/guard method.
However, in the interest of creating a minimal implementation, I think the solution would be overriding the prepareRequest method of the Application class.
In bootstrap/app.php replace
$app = new Laravel\Lumen\Application(
realpath(__DIR__.'/../')
);
with
$app = new class (realpath(__DIR__.'/../')) extends Laravel\Lumen\Application {
protected function prepareRequest(\Symfony\Component\HttpFoundation\Request $request)
{
if (! $request instanceof Illuminate\Http\Request) {
$request = Illuminate\Http\Request::createFromBase($request);
}
$request->setUserResolver(function () use ($request) {
return $request->bearerToken();
})->setRouteResolver(function () {
return $this->currentRoute;
});
return $request;
}
};
This way you can have the simple resolution logic for getting the bearer token (don't include the AuthServiceProvider then).
(This requires PHP 7 anonymous classes; alternatively just extend to a regular class).
You do not need to change the register() function.
Just uncomment the following lines in bootstrap/app.php file:
$app->withEloquent();
$app->register(App\Providers\AppServiceProvider::class);
$app->register(App\Providers\AuthServiceProvider::class);
$app->routeMiddleware([
'auth' => App\Http\Middleware\Authenticate::class,
]);
And in app/Providers/AuthServiceProvider.php->boot(), it has default method to retrieve the authenticated user.
$this->app['auth']->viaRequest('api', function ($request) {
if ($request->input('api_token')) {
return User::where('api_token', $request->input('api_token'))->first();
}
});
You may use an API token in the request headers or query string, a bearer token on the request, or using any other approach your application requires.
After that, you may retrieve the authenticated user like this:
use Illuminate\Http\Request;
$app->get('/post/{id}', ['middleware' => 'auth', function (Request $request, $id) {
$user = Auth::user();
$user = $request->user();
//
}]);
The rebinding method will add an additional reboundCallbacks which this callback will be triggered right after the abstract is rebound. As long as your abstract is not rebound, the reboundCallbacks are not called. So, you can simply rebound your abstract, like so:
$this->app->rebinding('request', function ($app, $request) {
$request->setUserResolver(function () use ($app) {
$token = $this->request->bearerToken();
dd($token);
// do the rest
});
});
// REBOUND HERE
$this->app->instance('request', $this->app->make('request'));
// TEST
// $this->app->make('request')->user(); // output is $token
Try uncomment the rebound line above, your dd will not called at all.
Extra
You can use refresh method (to register reboundCallbacks) combined with extend method (to rebound) for cleaner code:
public function register()
{
parent::register();
$this->app->refresh('request', $this, 'overrideUserResolver');
// REBOUND HERE, JUST ANOTHER WAY TO REBOUND
$this->app->extend('request', function ($request) { return $request; });
// TEST
$this->app->make('request')->user();
}
public function overrideUserResolver($request)
{
$request->setUserResolver(function ($guard = null) use ($request) {
$token = $request->bearerToken();
dd($token);
// do the rest
});
}
So, I'm building an API architecture and I am struck on the way the programmer will be implementing the output of the API endpoints.
The thing is that there can be different ways to store the response that I'm going to output and I'dont know if it would be worth to make an interface and force the developer to use just one way.
First case, using a class property.
We have a controller that makes a number of actions all based on a resource 'user' and then in a response handler function returns the formed response:
class LogInController implements ResponseInterface{
private $user;
private function setUser(){...};
private function checkThatToTheUser();{...}
function LogInEndpoint(){
$this->setUser();
$this->checkThatToTheUser();
return $this->handledResponse();
}
/**implemented function that doesn't
take parameters as it has access
to the class property that has the response formed*/
function handledResponse(){
$response = [
'user_id' => $this->user->id
];
return $this->APIResponse($response);
};
}
Second case, directly calling to the response manager.
We have a controller that simply outputs some data from the database, which in this cases can be a collection or a collection of collections.
class UserController{
function get(){
$users = User::paginate(20);
return $this->APIReponse($users);
}
function getByID($id){
$user = User::find($id);
return $this->APIReponse($user);
}
}
Third case, we encapsulate all the response generation on a function but having no properties.
In this case we pass the instanciated user to the response handler.
class UserActionsController{
private function get(){
$user = User::find(1);
return $user
}
private function getComplexThings($user){
...
return $user;
}
function getComplextDataEndpoint(){
$user = $this->get();
$user = $this->getComplexThings($user);
return $this->handledResponse($user);
}
function handledResponse($user){
...
return $this->APIResponse($user);
}
}
Should I let this open and let the programmer choose in the moment of the implementation or force the way it gets implemented (properties, parameters, directly outputs, outputs under a response handler...).
I am somewhat new to OOP, although I know about interfaces and abstract classes a bit. I have a lot of resource controllers that are somewhat similar in the bigger scheme of things, they all look like the example below, the only main difference is the index and what I pass to the index view.
What I simply need to know is, can I OO things up a bit with my resource controllers? For example, create one "main" resource controller in which I simply pass the correct instances using an interface for example? I tried playing around with this but I got an error that the interface wasn't instantiable, so I had to bind it. But that means I could only bind an interface to a specific controller.
Any advice, tips and pointers will help me out :)
class NotesController extends Controller
{
public function index()
{
$notes = Note::all();
return view('notes.index', compact('notes'));
}
public function create()
{
return view('notes.create');
}
public function show(Note $note)
{
return view('notes.show', compact('note'));
}
public function edit(Note $note)
{
return view('notes.edit', compact('note'));
}
public function store(Request $request, User $user)
{
$user->getNotes()->create($request->all());
flash()->success('The note has been stored in the database.', 'Note created.');
return Redirect::route('notes.index');
}
public function update(Note $note, Request $request)
{
$note->update($request->all());
flash()->success('The note has been successfully edited.', 'Note edited.');
return Redirect::route('notes.index');
}
public function delete($slug)
{
Note::where('slug', '=', $slug)->delete();
return Redirect::to('notes');
}
}
Note: Totally my opinion!
I would keep them how you have them. It makes them easier to read and understand later. Also will save you time when you need to update one to do something different from the rest. We tried this in a project I worked on and while granted it wasn't the best implementation, it is still a pain point to this day.
Up to you though. I'm sure people have done that in a way that they love and works great. Just hasn't been the case in my experience. I doubt anyone would look at your code though and criticize you for not doing it.
In Case you need to bind different Model instanses then you may use Contextual Binding, for example, put the following code in AppServiceProvider's register() method:
$this->app->when('App\Http\Controllers\MainController')
->needs('Illuminate\Database\Eloquent\Model')
->give(function () {
$path = $this->app->request->path();
$resource = trim($path, '/');
if($pos = strpos($path, '/')) {
$resource = substr($path, 0, $pos);
}
$modelName = studly_case(str_singular($resource));
return app('App\\'.$modelName); // return the appropriate model
});
In your controller, use a __construct method to inject the model like this:
// Put the following at top of the class: use Illuminate\Database\Eloquent\Model;
public function __construct(Model $model)
{
$this->model = $model;
}
Then you may use something like this:
public function index()
{
// Extract this code in a separate method
$array = explode('\\', get_class($this->model));
$view = strtolower(end($array));
// Load the result
$result = $this->model->all();
return view($view.'.index', compact('result'));
}
Hope you got the idea so implement the rest of the methods.
I have implemented following code to run a code on before any action of any controller. However, the beforeFilter() function not redirecting to the route I have specified. Instead it takes the user to the location where the user clicked.
//My Listener
namespace Edu\AccountBundle\EventListener;
use Symfony\Component\DependencyInjection\Container;
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
class BeforeControllerListener
{
public function onKernelController(FilterControllerEvent $event)
{
$controller = $event->getController();
if (!is_array($controller))
{
//not a controller do nothing
return;
}
$controllerObject = $controller[0];
if (is_object($controllerObject) && method_exists($controllerObject, "beforeFilter"))
//Set a predefined function to execute Before any controller Executes its any method
{
$controllerObject->beforeFilter();
}
}
}
//I have registered it already
//My Controller
class LedgerController extends Controller
{
public function beforeFilter()
{
$commonFunction = new CommonFunctions();
$dm = $this->getDocumentManager();
if ($commonFunction->checkFinancialYear($dm) == 0 ) {
$this->get('session')->getFlashBag()->add('error', 'Sorry');
return $this->redirect($this->generateUrl('financialyear'));//Here it is not redirecting
}
}
}
public function indexAction() {}
Please help, What is missing in it.
Thanks Advance
I would suggest you follow the Symfony suggestions for setting up before and after filters, where you perform your functionality within the filter itself, rather than trying to create a beforeFilter() function in your controller that is executed. It will allow you to achieve what you want - the function being called before every controller action - as well as not having to muddy up your controller(s) with additional code. In your case, you would also want to inject the Symfony session to the filter:
# app/config/services.yml
services:
app.before_controller_listener:
class: AppBundle\EventListener\BeforeControllerListener
arguments: ['#session', '#router', '#doctrine_mongodb.odm.document_manager']
tags:
- { name: kernel.event_listener, event: kernel.controller, method: onKernelController }
Then you'll create your before listener, which will need the Symony session and routing services, as well as the MongoDB document manager (making that assumption based on your profile).
// src/AppBundle/EventListener/BeforeControllerListener.php
namespace AppBundle\EventListener;
use Doctrine\ODM\MongoDB\DocumentManager;
use Symfony\Bundle\FrameworkBundle\Routing\Router;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
use AppBundle\Controller\LedgerController;
use AppBundle\Path\To\Your\CommonFunctions;
class BeforeControllerListener
{
private $session;
private $router;
private $documentManager;
private $commonFunctions;
public function __construct(Session $session, Router $router, DocumentManager $dm)
{
$this->session = $session;
$this->router = $router;
$this->dm = $dm;
$this->commonFunctions = new CommonFunctions();
}
public function onKernelController(FilterControllerEvent $event)
{
$controller = $event->getController();
if (!is_array($controller)) {
return;
}
if ($controller[0] instanceof LedgerController) {
if ($this->commonFunctions->checkFinancialYear($this->dm) !== 0 ) {
return;
}
$this->session->getFlashBag()->add('error', 'Sorry');
$redirectUrl= $this->router->generate('financialyear');
$event->setController(function() use ($redirectUrl) {
return new RedirectResponse($redirectUrl);
});
}
}
}
If you are in fact using the Symfony CMF then the Router might actually be ChainRouter and your use statement for the router would change to use Symfony\Cmf\Component\Routing\ChainRouter;
There are a few additional things here you might want to reconsider - for instance, if the CommonFunctions class needs DocumentManager, you might just want to make your CommonFunctions class a service that injects the DocumentManager automatically. Then in this service you would only have to inject your common functions service instead of the document manager.
Either way what is happening here is that we are checking that we are in the LedgerController, then checking whether or not we want to redirect, and if so we overwrite the entire Controller via a callback. This sets the redirect response to your route and performs the redirect.
If you want this check on every single controller you could simply eliminate the check for LedgerController.
.
$this->redirect() controller function simply creates an instance of RedirectResponse. As with any other response, it needs to be either returned from a controller, or set on an event. Your method is not a controller, therefore you have to set the response on the event.
However, you cannot really set a response on the FilterControllerEvent as it is meant to either update the controller, or change it completely (setController). You can do it with other events, like the kernel.request. However, you won't have access to the controller there.
You might try set a callback with setController which would call your beforeFilter(). However, you wouldn't have access to controller arguments, so you won't really be able to call the original controller if beforeFilter didn't return a response.
Finally you might try to throw an exception and handle it with an exception listener.
I don't see why making things this complex if you can simply call your method in the controller:
public function myAction()
{
if ($response = $this->beforeFilter()) {
return $response;
}
// ....
}
public function onKernelController(FilterControllerEvent $event)
{
$request = $event->getRequest();
$response = new Response();
// Matched route
$_route = $request->attributes->get('_route');
// Matched controller
$_controller = $request->attributes->get('_controller');
$params = array(); //Your params
$route = $event->getRequest()->get('_route');
$redirectUrl = $url = $this->container->get('router')->generate($route,$params);
$event->setController(function() use ($redirectUrl) {
return new RedirectResponse($redirectUrl);
});
}
Cheers !!