Why does my page's request require authentication? - php

My problem is concerned with:
a blog homepage called via the ArticlesController#index function
the individual blog post pages called via ArticlesController#show function, and
the comment form to be embedded into the individual blog post pages that would post to blog/{blog}/comment and would invoke the CommentsController#store method
I have the following routes of concern defined in my routes.php file:
Route::resource ('blog', 'ArticlesController');
Route::resource ('blog/{blog}/comment', 'CommentsController');
The ArticlesController is set to call the auth middleware for all functions except index() and show($id):
public function __construct()
{
$this->middleware('auth', ['except' => 'index', 'show']);
}
When I attempt to access the individual blog post pages without the comment form, it works as expected and allows me to access the homepage and individual post pages without authentication.
Whereas when I have the comment form embedded into the individual post pages, it allows me to access the homepage but demands me to authenticate before I could access the individual post pages.
Can anyone tell me why it behaves so even though my CommentsController is a separate entity and it is not invoking the auth middleware?

The except argument should be an array
public function __construct()
{
$this->middleware('auth', ['except' => ['index', 'show']]);
}
Updated per comment
If you look at the code for the middleware method if is looking for an array.
/**
* Register middleware on the controller.
*
* #param string $middleware
* #param array $options
* #return void
*/
public function middleware($middleware, array $options = [])
{
$this->middleware[$middleware] = $options;
}
As to why it worked before, who knows. I imagine that at some point along the chain of methods Laravel is converting a string to an array and that is why it worked.
You might want to consider writing a test for your controller. That way you are not dependent on any particular moment of how it is working. You know it works the way you intended and that nothing you do changes the expected result.

Related

Laravel 7 User data in __construct

Using Laravel 7.
In the controller constructor, I then hoped to get access to the current user details so I could load main site widgets (buttons links etc) and custom user widgets in to one to be displayed in the view
use Illuminate\Support\Facades\Auth;
...
$widgets = Cache::get("widgets");
$usersdata = Cache::get("userdata");
$this->middleware('auth');
$widgets = array_merge($widgets, $usersdata[Auth::user()->id]["widgets"]);
View::share([
"widgets" => json_encode($widgets)
]);
however at this stage from research the user data is not available (even after authentication ?).
Not sure of best way to access this, or better practice might be to override the middleware auth (where?) so that it could return user id or something eg:
$userid=$this->middleware('auth');
I would like this in the constructor so the same method is in place for all controllers which extend this main controller.
This is intended behavior from laravel, you can read more about it here.
Laravel collects all route specific middlewares first before running
the request through the pipeline, and while collecting the controller
middleware an instance of the controller is created, thus the
constructor is called, however at this point the request isn’t ready
yet.
You can find Taylor's reasoning behind it here:
It’s very bad to use session or auth in your constructor as no request
has happened yet and session and auth are INHERENTLY tied to an HTTP
request. You should receive this request in an actual controller
method which you can call multiple times with multiple different
requests. By forcing your controller to resolve session or auth
information in the constructor you are now forcing your entire
controller to ignore the actual incoming request which can cause
significant problems when testing, etc.
So one solution would be to create a new middleware and then apply it to all routes, something like this, where widgets is your new middleware:
Route::group(['middleware' => ['auth', 'widgets']], function () {
// your routes
});
But if you really want to keep it in the constructor you could implement the following workaround:
class YourController extends Controller
{
public function __construct(Request $request)
{
$this->middleware('auth');
$this->middleware(function ($request, $next) {
$widgets = Cache::get("widgets");
$usersdata = Cache::get("userdata");
$widgets = array_merge($widgets, $usersdata[$request->user()->id]["widgets"]);
View::share([
"widgets" => json_encode($widgets)
]);
return $next($request);
});
}
}

Limit a view to only invited users Laravel Middleware

I am looking for a way to show a specific view only to specific visitors who get a link to that view. How can I make a middleware so that shows the view only if it comes from a specific source (like if it comes from source.blade.php)
I cannot use the middleware for guest or auth, because then it would give that view to all the auth, but I only want give that view to an auth who has made a payment at beginning and have been redirected from a specific URL.
How can I setup a middleware in such a way that it only shows the view if the auth is being redirected from another view like - source.blade.php
Currently, I have this page setted up like this
public function __construct()
{
$this->middleware('auth:client');
}
This works well, it only shows this page to someone who has logged in from the client authentication guard, but the problem is, any client can visit this page.
I am looking for a way to make it so that it can viewed only by the client who paid at the beginning, and were re-directed by my website. Maybe something like
public function __construct()
{
if(redirect_source="source.blade.php") {$this->middleware('auth:client'); }
}
I think you want a solution that will limit the permission based on your user type.
Middlewares are used to condition certain parameters if you want to let the requester to go into the specific url/route and not to control inside your views.
So if you want to control it, you can use this solution .
namespace App\Laravel\Middleware\Backoffice;
use Closure;
use Illuminate\Contracts\Auth\Guard;
use Illuminate\Http\RedirectResponse;
use Auth, Session;
class ValidSuperUser {
/**
* The Guard implementation.
*
* #var Guard
*/
protected $auth;
/**
* Create a new filter instance.
*
* #param Guard $auth
* #return void
*/
public function __construct(Guard $auth)
{
$this->auth = $auth;
}
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if($this->auth->user()->type != "super_user") {
Session::flash('notification-status','failed');
Session::flash('notification-title',"Access Denied");
Session::flash('notification-msg','You are not allowed to view the page you are tring to access.');
return redirect()->route('backoffice.dashboard');
}
return $next($request);
}
}
in your Kernel.php under Http folder declare the new Middleware in order to use.
**put it under protected $routeMiddleware = []
and then use it to your routes that need to help that kind of user type.
$route->group(['middleware' => "aliasofyournewmiddle"],function(){
//some routes here
});
your new middleware can be any condition upon the request, so any inputs and available session that has been passed to that url are usable on that middleware, adjust it on how you want to handle the situation.
You can pass a token when redirecting your users to your specific page. Then use your middleware to check whether that token is valid or not.
Say for example, someone made a payment at beginning, you store a hash value of that person's user id or any unique identifier in a session, then redirect the user with the same hash value included in your url. Your middleware can then handle the validation, if the value stored in the session is the same with the value provided in the url.

Laravel - How to restrict certain functionality based on user authentication?

Laravel newbie here (obviously :D ). I've set up a new model & controller for a model named Pages.
Every User has many Pages.
Each Page has a single User.
I've created the following functioning controller actions (& views):
PagesController::index
PagesController::create
PagesController::store
PagesController::show
PagesController::edit
PagesController::delete
So you can edit a Page by going to url.dev/pages/{id}/edit.
The problem is, you can access all of these routes regardless of your session status. So random users can edit any given Page. Which, obviously, is terrible.
Can anyone point me in the direction of what I should read up on, to limit the ability to access my model's controller actions based on whether or not the user is logged in (and if it's the correct user, at all)?
To force a specific route to be only accessible by authenticated users you can specify middleware auth in the controller constructor, like so:
public function __construct()
{
$this->middleware('auth');
}
Also you can restrict which methods you want auth to be applied to in the controller, using the only or except parameters. Using only you could do:
public function __construct()
{
$this->middleware('auth', ['only' => ['create', 'store', 'edit', 'delete']]);
}
You´re looking for middleware..
You can read more here
public function __construct()
{
$this->middleware('auth')->only('index');
$this->middleware('admin')->except('store');
}
Other answers are good, but I prefer to use middleware on route groups.
So when I have several routes like this:
Route::get('pages', 'PagesController#index')->name('pages.index');
Route::get('pages/{id}/edit', 'PagesController#edit')->name('pages.edit');
I would add them inside Laravel Route group. Like this:
Route::group(['middleware' => 'auth'], function() {
Route::get('pages', 'PagesController#index')->name('pages.index');
Route::get('pages/{id}/edit', 'PagesController#edit')->name('pages.edit');
});

show/hide (laravel) menus depending on roles

I want to have certain menu options visible only to certain users.
I've modified my user table in the standard auth framework by adding two roles - boolean columns: isTeacher and isOnCommittee.
I thought I'd try
create a method in my controller to check if the logged in user has the specific role, and then
in the view, call the method, and show the menu option, (or not).
It's all well and fine to put a #if (Auth::iSTeacher()) into my view, but where do I put my function?
I did a search for guest() across all files and found this
...\vendor\laravel\framework\src\Illuminate\Contracts\Auth\Guard.php:
public function guest();
/**
* Get the currently authenticated user.
*
* #return \Illuminate\Contracts\Auth\Authenticatable|null
*/
I understand the purpose of a guard is protect a route, so this is not the place for it.
Where am I supposed to be creating my function?
(if you didn't guess - I'm very new to laravel.
My preferred way to do this would be with Authorization via the Gate Facade. You can define so-called "abilities" in AuthServiceProvider like this:
public function boot(GateContract $gate)
{
$this->registerPolicies($gate);
$gate->define('update-post', function ($user, $post) {
return $user->id === $post->user_id;
});
}
Than inside views you can check:
#can('update-post', $post)
Edit Post
#endcan
Source:
https://laravel.com/docs/5.2/authorization#within-blade-templates
You can use Query Scopes functions in your User model. e.g. write your isTeacher() in User model and in view check
#if(Auth::user()->isTeacher)
YOUR CODE HERE
#end if

Get current controller and action id in Yii

I want to force all users to log in before accessing pages of my site. I have followed Larry Ullman's tutorial Forcing Login for All Pages in Yii.
According to the tutorial you can make an exception for some pages to avoid redirecting to the log in page. In order to check the current controller it has checked $_GET value. My problem is that I have used urlManager to rewrite the URL and $_GET gives me a null value. Is there any method I can use to get the current controller and action in the score of my class?
I tried the following but it is not accessible in the scope of my component class:
Yii::app()->controller->getId
Did you try:
Yii::app()->controller->id
and:
Yii::app()->controller->action->id
?
Yes you can get the current controller/action route, by reversing urlManager rule:
Yii::app()->urlManager->parseUrl(Yii::app()->request)
As now in Yii2
get current controller name
Yii::$app->controller->id
current controller object
Yii::$app->controller
current action name:
Yii::$app->controller->action->id
current route:
Yii::$app->requestedRoute
Using Yii2, obtain the current controller object with:
Yii::$app->controller
From the controller, obtain the current action as a string using:
Yii::$app->controller->action->id
In Yii2:
The problem of calling Yii::$app->controller->id is that when you call it somewhere (example: in one of your top-level abstract controller), Yii::$app->controller might not be instantiated yet, so it will return error.
Just directly call urlManager to map request to route:
var_dump(Yii::$app->urlManager->parseRequest(Yii::$app->request))
Try Yii::app()->controller->getRoute()
If I get you question correctly, you are basically trying to stop access to certain actions in the controller from being accessed without being logged in right?
If this is what you are after, the correct method to do it is this :
Make a actionMethod() in the controller like so :
class SomeController extends CController{
public function actionSomeAction(){
... More code...
}
After that, you can access the site using : path/to/application/controllerName/actionName
Now if you want to force the user to log in before accessing the action, do this :
Make an access control like so :
/**
* #return array action filters
*/
public function filters()
{
return array(
'accessControl', // perform access control for CRUD operations
);
}
/**
* Specifies the access control rules.
* This method is used by the 'accessControl' filter.
* #return array access control rules
*/
public function accessRules()
{
return array(
array('allow', // allow authenticated user to perform 'create' and 'update' actions
'actions' => array('**yourActionMethodName**'),
'users' => array('#'),
),
array('deny', // deny all users
'users' => array('*'),
),
);
}
Now only authenticated users would be able to access the URL.
I hope it solved your problem.
If you simply want to check if the user is a guest and if he is, send him to the login page everytime:
In the config/main.php, add the following :
'defaultController' => 'controllerName/actionMethod',
And in that controller just add the above access rule. Now, by default you are opening the site to an access controlled method. So it would automatically redirect you to the login page.
Even another method :
Just add this in the views/layouts/main.php
<?php
if(Yii::app()->user->isGuest)
{
$this->redirect('/site/login');
}
?>
if (Yii::$app->requestedAction->id == "index") {
//do something
}

Categories