I have an interesting problem I need to solve and looking for best way to approach it.
Have a laravel 5.4 app that is fronted by a few public static pages. App is a golf performance tracking app. User enters data and the app does some analysis and shows graphs etc.
Problem is that if a new user signs up they have no data and they need to enter a score for the app to "work"
User signs up, verifies email, etc. so they now have authorized access to the app.
What I want to do is check if the user has any scores in the db (fine I can do that easily) then if they have at least one score entered send them to the app. Fine.
If they have no scores I want to send them to a view that explains that they need to have to enter a score and present the form to do so.
Question is:
where is the best place to check if the user has no scores and then send them to the view to enter the score and not allow them to access any of the app views.
Looking for the best way to do this? Is it create the condition and check the db in the routes/web.php? Is there a better way to accomplish it? and if so what do I do with the routes that sit in:
Route::group(['middleware' => ['web','auth']], function () {
Create a custom middleware and check if the user has score and redirect to any route or display any view you want. Something like this.
public function handle($request, Closure $next)
{
$user = $request->user();
if ($user && ($user->score == 0)) {
return redirect()->route('instruction');
}
return $next($request);
}
Apply this new middleware to all the routes which shouldn't be accessed by users with no scores. Also make sure you use this middleware after auth and not before.
Related
I have a Laravel application, with a login form. I am using subdomains, for example: {{business_subdomain}}.laravel.test. The purpose of my application is create companies in my database, and make registered users login on their attached company and specific domain.
So in my web.php I declared the following:
Route::domain('{business_subdomain}.' . env('APP_URL'))->middleware(['business.subdomain'])->group(function ()
My database contains the following tables:
* Standard tables like users
* Businesses which contains id, name and subdomain
* Business_user which contains business_id and user_id
The business_id (business_user table) has a relationship with id (business table)
The user_id (business_user table) has a relationship with id (users table)
I created a middleware that checks the following:
$business = $request->user()->businesses->where('subdomain', $request->route('business_subdomain'))->first();
So the subdomain value (business table) must be the equal to the URL/route subdomain value, otherwise when you login, you will get an 403 page.
So what I want to achieve: When a user has an account, but is not attached to the right company/business, I want to display the standard authentication error: These credentials do not match our records.
I've tried to use the code in my middleware on the LoginController and override almost every function separately, but it didn't work.
Do I need to override Laravel authentication functions, do I need to create another middleware or use Guards?
The laravel authentication login order (the order the application uses for logging in a user) seems very confusing for me.
Maybe someone can provide me with more information or help me out!
I tried to use the code in my middleware on the LoginController and override almost every function separately, but it didn't work.
Sounds like you might be looking for multi-tenancy. There are a couple different packages available, both for single and multi-database setups.
Here are some very informative slides on the topic, specific to Laravel. And an excellent article for more detail.
We have a similar application with subdomain routing using torzer/awesome-landlord. The tenant package ensures that login will only search for users of the correct tenant.
To set the correct tenant we added a middleware (App is the tenant, in your case it would be Business):
public function handle($request, Closure $next)
{
$subdomain = $this->getSubdomain($request->server('HTTP_HOST'));
if ($subdomain) {
$app = App::getBySlug($subdomain);
abort_if(!$app, 404);
$this->tenantManager->addTenant($app);
} else {
// empty or generic subdomain
// this is OK as long as there is no user
if ($user = $request->user()) {
return $this->redirectToUserAppSubdomain($request, $user);
}
}
return $next($request);
}
all my fellow friends, i have a question.
Route::group([
'middleware' => ['ensure.role:store', 'auth:api']
]
For simplification,
i have two roles : ADMIN and STORE
I have created a middleware that will validate user role, and if the user role is correct, then will allow the user to access the route.
It works fine.
I tried using ADMIN Jwt Token to access STORE routes, and rightfully i am kicked out, and vice versa.
But now, if i modify the token, lets say i add a string to any part of the token, and try to access any route, actually i am allowed to.
I tried var_dump and print something on the related middleware, and here are my observation.
1. If the token is VALID as one of the user role, then
the var_dump is executed, (means the middleware is executed)
2. if the token is INVALID as in i add / modify the original
token, then the var_dump is not executed, and so are the
others route middleware.
I am wondering what causes this behavior, and what could be the fix for this issue, as i need to throw 401 unauthenticated in any token invalid case.
Thank you
I figured it out.
After series of testing and reading, i found out that after laravel 5.3 and above, constructor is called before middleware. And because in my constructor i am using an user object before i am authenticated by the middleware, i encountered constructor error, because user is null.
Of course it is a bad practice to use user object in the construct, however due to the convenience of usage, i still decided to use it.
It sounds complex to use closure based middleware as alternative solution
So i use a workaround to do it.
I create a helper function that return me true if there is an user object or return abort(401); if there is no user object, then add this one line to all the constructors.
$this->checkAccess = EnsureRoleUtil::check('admin');
After that, i just do my next constructor as normally
public function __construct() {
$this->checkAccess = EnsureRoleUtil::check('admin');
$this->user = Auth::user();
$this->categoryM = new CategoryManager($this->user);
}
However, to be noted, it is not a good practice, it is just a hack / workaround.
I'm using this line in some controller's __construct
$this->middleware('auth');
This results, that every not logged user will be redirected to login page. It's of course, ok, but there is a problem.
I have two groups of users. In database I have a column called "role", which is boolean. 0 means basic users and 1 means admins. How can I treat, that entrance to some of controllers will be allowed only for admins? I really don't know how to do that in pretty way.
You can pass things to the middleware, like
$this->middleware('auth:1');
Now in the middleware you can check to see if the authenticated user has a role that you passed (in the example, 1). If they don't have the role that you require, then you can redirect them to the login screen or however you want to handle it.
you can use the following code to get the authenticated user and then write custom logic.
if(Auth::user()->role==0)
{
//you are basic user
}
esle if(Auth->user()->role==1)
{
//you are admin
}
you can also Gates and Policies for this type of work.
I'm building an app where a logged in user can edit their own profile and of course unable to edit another user's profile.
I have this following route :
http://2mark.dev/profile/derp22/edit
The problem with that route is that the route is accessible by another users and and will finally allow the other users to the edit "derp22" profile data. My question is how to protect this route so that the other users can't access it ?
Thanks for the help!
You can check this in the method in your profile controller.
Here is the methodology:
1. Get the current user.
2. Get the user you want to edit.
3. Check against those Id's.
4. Throw/return error message or proceed in method.
Example:
$currentUser = \Auth::user();
$user = User::findOrFail($id);
if ($user->id != $currentUser->id) {
abort(403);
}
// Then have the save method.
https://github.com/jeremykenedy/laravel-auth/blob/master/app/Http/Controllers/ProfilesController.php
Check this https://laravel.com/docs/5.4/authorization#via-the-user-model
or
You can compare if the authenticated user id is equal the id
hi typically a codeigniter mvc controller accepts an id as a parameter for a controller function. For example:
/photo/edit/1
A user would edit image id 1 from the photo controller. What is the best practice to prevent a user from editing someone else’s image..for example editing id 2? Restriction has to include more than just verifying a logged in user, because a logged in user will still be able to edit image 2.
I was thinking that i would write a library that implements a permission function, that is called on all controller functions. I would pass the user id and the url to the library function which would contain logic to verify if a user could execute that function or in this case edit an image.
The problem i see is..it will be tedious to write the logic code for all functions of my site, as each is different logic. Is there a better generally practiced way?
You generally handle the checking in the controller itself, so if, for instance you have the currently logged in user, and the photo object from the db, just store the photo's owner in the db record and compare them.
It really is not all that tedious, you are simply verifying ownership of the object before processing actions on it.
example...
if ($the_user->id == $the_photo->owner_id) {
//allow their actions
}
else {
redirect('/'); //if not, kick em out
}