How to use the middleware for restricting other content based on the role? Using React Js and Laravel - reactjs

I currently setup the role based and content authorization. However i got problem when I try the to access the route of super admin still accessible. I used this instruction https://medium.com/justlaravel/how-to-use-middleware-for-content-restriction-based-on-user-role-in-laravel-2d0d8f8e94c6 - but still no working well.
to understand well, I will share to you what suppose to be the output and my sample coding on my middleware and api.php
I have Middleware of CheckRoleRouter.php
I create a unauthorized.blade.php file for new response if the user role access wrong route.
I already set the middleware to the kernel.php
Role: I have Super Admin = 0 and Admin = 1
Middleware:
<?php
namespace App\Http\Middleware;
use Closure;
use Auth;
class CheckRoleRouter
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
//Role of users
//Super Admin = 0
//Admin = 1
// how to make a middleware to prevent accessing other router based on their role.
if ($request->user() && $request->user()->role != 0)
{
return new Response(view('unauthorized')->with('role', 'SUPERADMIN'));
}
return $next($request);
}
}
Api:
Route::group(['middleware' => 'App\Http\Middleware\CheckRoleRouter'], function()
{
Route::get('list_offers','Api\BusinessOffersController#list_offers');
});
Kernel:
'checker' => \App\Http\Middleware\CheckRoleRouter::class,
Session: - it means i used the admin role so the logic is the router of super_admin is not accessible anymore because i used the admin.
Page:

Related

How to disable authorization middleware in cakePHP4?

By default, the authorization plugin is apply to a global scope. For some controllers that I did not want to apply any authorization. I have to use the skipAuthorization config manually for each action. For authentication plugin, I can just only load the authentication component for each controller that requires authentication. However, the authorization middleware seems will always work even if I did not load the authorization component in the controller. So, why is that? And is there a way I can disable the authorization process for the entire controller?
You probably mean Authentication and not Authorization. In any case, from the Docs:
// in src/Controller/AppController.php
public function initialize()
{
parent::initialize();
$this->loadComponent('Authentication.Authentication');
}
By default the component will require an authenticated user for all
actions. You can disable this behavior in specific controllers using
allowUnauthenticated():
// in a controller beforeFilter or initialize // Make view and index not require a logged in user.
$this->Authentication->allowUnauthenticated(['view', 'index']);
More information: The Authentication plugin in the Cake Book.
I think you are not doing it in the right way. For authorization, you have to write a request policy. Whenever you bake controller just add --prefix Admin or whatever you want to.
cake bake controller Users --prefix Admin
Put all admin controllers in one place.
Add routes in your routes file
$builder->prefix('Admin',['_namePrefix' => 'admin:'], function (RouteBuilder $builder) {
$builder->connect('/', ['controller' => 'Users', 'action' => 'Index']);
$builder->fallbacks(DashedRoute::class);
});
`
Request Policy. Create a role table and add column role_id in the Users table and the rest you will understand with code below.
<?php
namespace App\Policy;
use Authorization\IdentityInterface;
use Authorization\Policy\RequestPolicyInterface;
use Cake\Http\ServerRequest;
class RequestPolicy implements RequestPolicyInterface
{
/**
* Method to check if the request can be accessed
*
* #param IdentityInterface|null Identity
* #param ServerRequest $request Server Request
* #return bool
*/
public function canAccess($identity, ServerRequest $request)
{
$role = 0;
if(!empty($identity)){
$data = $identity->getOriginalData();
$role = $data['role_id'];
}
if(!empty($request->getParam('prefix'))){
switch($request->getParam('prefix')){
case 'User' : return (bool)($role === 3);
case 'Admin': return (bool)($role === 1) || (bool)($role === 2);
}
}else{
return true;
}
return false;
}
}
`
and then implements AuthorizationServiceProviderInterface to the Application
use App\Policy\RequestPolicy;
use Authorization\AuthorizationServiceProviderInterface;
use Authorization\AuthorizationService;
use Authorization\Policy\MapResolver;
use Cake\Http\ServerRequest;
use Psr\Http\Message\ServerRequestInterface;
class Application extends BaseApplication implements AuthorizationServiceProviderInterface{
public function getAuthorizationService(ServerRequestInterface $request): AuthorizationServiceInterface
{
$mapResolver = new MapResolver();
$mapResolver->map(ServerRequest::class, RequestPolicy::class);
return new AuthorizationService($mapResolver);
}
}

.NET Core 3.1 web application with React - how to prevent access based on Active Directory group

I have a .NET Core 3.1 web application with React using windows authentication.
When a user enters their Active Directory credentials i would like to verify they belong to a particular Active Directory group before allowing access to the React app.
I have tried setting the default endpoint to a Login Controller to verify the user's groups but i don't know how to redirect to the React app if they do have the valid group.
Startup.cs:
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller}/{action=Index}/{id?}",
defaults: new { Controller = "Login", action = "Index" });
});
LoginController:
public IActionResult Index()
{
if (HttpContext.User.Identity.IsAuthenticated)
{
string[] domainAndUserName = HttpContext.User.Identity.Name.Split('\\');
//AuthenticateUser verifies if the user is in the correct Active Directory group
if (AuthenticateUser(domainAndUserName[0], domainAndUserName[1]))
{
//This is where i would like to redirect to the React app
return Ok(); //This does not go to the react app
return LocalRedirect("http://localhost:50296/"); //This will keep coming back to this method
}
return BadRequest();
}
}
Is it possible to redirect to the React app from the controller?
Is there a better way to verify an active directory group, possibly through authorizationService.js?
I've been in this situation before, and solved it with custom implementation of IClaimsTransformation. This approach may also be used with OpenId Connect and other authentication systems that requires additional authorization.
With this approach, you can use authorize attribute on controller that serves your React app
[Authorize(Roles = "HasAccessToThisApp")]
and
User.IsInRole("HasAccessToThisApp")
elsewhere in code.
Implementation. Please note that TransformAsync will be called on every request, some caching is recommended if any time-consuming calls.
public class YourClaimsTransformer : IClaimsTransformation
{
private readonly IMemoryCache _cache;
public YourClaimsTransformer(IMemoryCache cache)
{
_cache = cache;
}
public Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal incomingPrincipal)
{
if (!incomingPrincipal.Identity.IsAuthenticated)
{
return Task.FromResult(incomingPrincipal);
}
var principal = new ClaimsPrincipal();
if (!string.IsNullOrEmpty(incomingPrincipal.Identity.Name)
&& _cache.TryGetValue(incomingPrincipal.Identity.Name, out ClaimsIdentity claimsIdentity))
{
principal.AddIdentity(claimsIdentity);
return Task.FromResult(principal);
}
// verifies that the user is in the correct Active Directory group
var domainAndUserName = incomingPrincipal.Identity.Name?.Split('\\');
if (!(domainAndUserName?.Length > 1 && AuthenticateUser(domainAndUserName[0], domainAndUserName[1])))
{
return Task.FromResult(incomingPrincipal);
}
var newClaimsIdentity = new ClaimsIdentity(
new[]
{
new Claim(ClaimTypes.Role, "HasAccessToThisApp", ClaimValueTypes.String)
// copy other claims from incoming if required
}, "Windows");
_cache.Set(incomingPrincipal.Identity.Name, newClaimsIdentity,
DateTime.Now.AddHours(1));
principal.AddIdentity(newClaimsIdentity);
return Task.FromResult(principal);
}
}
In Startup#ConfigureServices
services.AddSingleton<IClaimsTransformation, YourClaimsTransformer>();

Real time react web app with pusher and laravel

I want to use pusher for realtime chat and it works properly with public channel but when I use private channel I got this error :
pusher.js:1333 Cross-Origin Read Blocking (CORB) blocked cross-origin response http://20.30.0.236:8000/login with MIME type text/html
this is laravel code :
Event :
use Dispatchable, InteractsWithSockets, SerializesModels;
/**
* Create a new event instance.
*
* #return void
*/
public $user;
public $message;
public function __construct(User $user, Message $message)
{
$this->user = $user;
$this->message = $message;
}
/**
* Get the channels the event should broadcast on.
*
* #return \Illuminate\Broadcasting\Channel|array
*/
public function broadcastOn()
{
return new PrivateChannel('chat');
}
channels.php :
Broadcast::channel('private-chat', function ($user) {
return true;
});
BroadcastServiceProvider :
class BroadcastServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
Broadcast::routes(['middleware' => ['auth:api']]);
require base_path('routes/channels.php');
}
}
and this is react js code :
export const onChatRcv = () => {
try {
Pusher.logToConsole = true;
var pusher = new Pusher('83*********63c912f5', {
cluster: 'ap2',
forceTLS: true,
authTransport: 'jsonp',
authEndpoint: `${baseUrl}broadcasting/auth`,
headers: {
'Authorization' : `Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImp0aSI6IjRhZTA1YjM2ZGNhN2I5NWI4NTJiZjFhOWRiZTQ5ZWE1NzFmNTNkMTE4NWQyOWU0Mjk0ZDI5NmJmZThhZTE0OGQzNzcwODM1MjEzYTg2NzA1In0.eyJhdWQiOiIxIiwianRpIjoiNGFlMDViMzZkY2E3Yjk1Yjg1MmJmMWE5ZGJlNDllYTU3MWY1M2QxMTg1ZDI5ZTQyOTRkMjk2YmZlOGFlMTQ4ZDM3NzA4MzUyMTNhODY3MDUiLCJpYXQiOjE1NTExMDQ3NTYsIm5iZiI6MTU1MTEwNDc1NiwiZXhwIjoxNTgyNjQwNzU2LCJzdWIiOiI1Iiwic2NvcGVzIjpbXX0.HOnNyhQQ48Hj4AZdP5vS5Zd5AfUr5XNP4zgrgR_f2-aAgFw4eWrNeHQSfdJt071_ChRINmv5W7O1LExxGIvCoSjiYFYPmw_8WjdFI_81WHoqM69ve-bgriK6eO1Yf0N3v3fc1DvPk2ZFYXXDmQbMLLXUyUqfjoYGty8AMgxCDulZ1tRMZ2rOVQZJ0ePbTw1eHQdMzBWG36fXWEbczLR99-_Dn8ta8P6iq0XWDr0cimlFzdHsG66iMeI0xWCJ1DRbxzr2LuX0j5zKe0j0_WNZJNbAFfeY87m7FDHjbHTNB1IB9Meh8kITV1mPQLc2n812j2QgW19KKWgpgZcy4tlfIBfT0x-aQAMkIUtmcHW0aEJ8RkHWKZYhyQ8yV61RIL3IxLpepHUVds8CZnxDGQ2NQ4bmb8UE7xQkV-KpmF5fZ0NCCxMuMpYdVkd0t9gc_Jra07_Sq7HbEJHEZbPCfhbDscAZQr2U9ddVaKwiGuFjSGXvOKS_lUAB91lBWada3k15FG2XoBfAv94mai2aWo41sep0nmlBKXPCVbWiczbeNL6ZXm_aE-tkLNS-Pc0veXogxZIaKVhFnRsW5qHTXI8v6sU6Nd9pzrIe173FqXQtzpA_tqrmdWU-lU-u484hWkPn2OcQcSckANpx-7_EVhrAPSfV7-WWamMRp2EC-3uFpmQ`,
},
});
var privateChannel = pusher.subscribe('private-chat' );
privateChannel.bind('App\\Events\\MessageSent', function(data) {
console.log(data);
});
} catch (error) {
console.error(error);
}
}
what is the problem?
it works when we use public channel but in private channel, we got this warning
Cross-Origin Read Blocking (CORB) blocked cross-origin response http://20.30.0.236:8000/login with MIME type text/html
The default route broadcasting/auth can't retrieve a suitable response so I added the custom authEndPoint.
web.php:
Route::get('pusher/auth', 'PusherController#pusherAuth');
and added PusherController:
class PusherController extends Controller
{
/**
* Authenticates logged-in user in the Pusher JS app
* For presence channels
*/
public function pusherAuth()
{
$user = auth()->user();
if ($user) {
$pusher = new Pusher('auth-key', 'secret', 'app_id');
$auth= $pusher->socket_auth(Input::get('channel_name'), Input::get('socket_id'));
$callback = str_replace('\\', '', $_GET['callback']);
header('Content-Type: application/javascript');
echo($callback . '(' . $auth . ');');
return;
}else {
header('', true, 403);
echo "Forbidden";
return;
}
}
}
This works and subscribes the channel.
You can think of accessing a private channel as if you are making a private auth request to the server .
You Cant Directly Access A private channel from react Because of Security Reasons.
As Mentioned In CodeAcademy ....
Servers are used to host web pages, applications, images, fonts, and much more. When you use a web browser, you are likely attempting to access a distinct website (hosted on a server). Websites often request these hosted resources from different locations (servers) on the Internet. Security policies on servers mitigate the risks associated with requesting assets hosted on different server
You Need a policy in your laravel app to add CORS (CROSS ORIGIN REQUEST SHARING )
Initially It was a bit complicated But You can use this library.
Now You can make any kind of private requests to your laravel app.
PS
Dont Forget to add checks in your broadcasts routes in channels.php as you are just simply returning true without any checks.

Laravel 5 Password Reset with Angular View

I am trying to use the Laravel inbuilt password reset in my app where Laravel 5.1 acts as the backend api and Angular 1.3 for all front-end views. I have set-up the Password reset as per the docs where I have done the following:
1) Create the table
php artisan migrate
2) Added this to the route:
Route::post('password/email', 'Auth/PasswordController#postEmail');
Route::post('password/reset', 'Auth/PasswordController#postReset');
Since I will be using Angular to display frontend forms, I did not add the views for GET. I havent done any changes to the Auth/PasswordController.php and right now its just like the way it came. But when I test the above URL from Postman POST request, I am getting the error:
View [emails.password] not found.
How can I let Angular Handle the views and not have Laravel worry about the view? Do I have to have Laravel View for the inbuilt password reset to work? How do I approach this?
Override the postEmail and postReset methods so that they return a JSON response (don't let it redirect). Subsequently post to /password/email and /password/reset from Angular via xhr.
Open app/Http/Controllers/Auth/PasswordController.php
<?php namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
class PasswordController extends Controller
{
use ResetsPasswords;
//add and modify this methods as you wish:
/**
* Send a reset link to the given user.
*
* #param \Illuminate\Http\Request $request
* #return \Illuminate\Http\Response
*/
public function postEmail(Request $request)
{
$this->validate($request, ['email' => 'required|email']);
$response = Password::sendResetLink($request->only('email'), function (Message $message) {
$message->subject($this->getEmailSubject());
});
switch ($response) {
case Password::RESET_LINK_SENT:
return redirect()->back()->with('status', trans($response));
case Password::INVALID_USER:
return redirect()->back()->withErrors(['email' => trans($response)]);
}
}
/**
* Reset the given user's password.
*
* #param \Illuminate\Http\Request $request
* #return \Illuminate\Http\Response
*/
public function postReset(Request $request)
{
$this->validate($request, [
'token' => 'required',
'email' => 'required|email',
'password' => 'required|confirmed',
]);
$credentials = $request->only(
'email', 'password', 'password_confirmation', 'token'
);
$response = Password::reset($credentials, function ($user, $password) {
$this->resetPassword($user, $password);
});
switch ($response) {
case Password::PASSWORD_RESET:
return redirect($this->redirectPath());
default:
return redirect()->back()
->withInput($request->only('email'))
->withErrors(['email' => trans($response)]);
}
}
}
Ceckout your path to views folder in app\bootstrap\cache\config.php at section "view"
'view' =>
array (
'paths' =>
array (
0 => '/home/vagrant/Code/app/resources/views',
),
'compiled' => '/home/vagrant/Code/app/storage/framework/views',
),
this path MUST be at SERVER! not at you local mashine like
"D:\WebServers\home\Laravel\app\bootstrap\cache", if you use the homestead.
And You must use command like: "php artisan config:clear | cache" at SERVER!
I had the same problem than you. You could manage to change the view in config/auth.php if you have another one not in resources/views/emails/password.blade.php.
Because this view isn't created by default, that's why you got the error.

Bug into fosuserbundle when double click on confirmation link?

I just begin to use fosuserbundle, today I activate the confirmation register link.
It works great, but if the user click a second time on the confirmation link in the email, he get that error :
The user with confirmation token "3hiqollkisg0s4ck4w8g0gw4soc0wwoo8ko084o4ww4sss8o4" does not exist
404 Not Found - NotFoundHttpException
I think this error should be handle by the bundle, no ?
Thanks
Here's the code for overriding the action. Basically just copied part of the actual FOS action and modded.
Create a RegistrationController.php file in your user bundle's controller folder and put the overriding RegistrationController class in there.
Assuming your user bundle is Acme\UserBundle:
<?php
// Acme\UserBundle\RegistrationController.php
namespace Acme\UserBundle\Controller;
use Symfony\Component\HttpFoundation\RedirectResponse;
use FOS\UserBundle\Controller\RegistrationController as BaseController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
class RegistrationController extends BaseController
{
/**
* Receive the confirmation token from user email provider, login the user
*/
public function confirmAction(Request $request, $token)
{
$userManager = $this->container->get('fos_user.user_manager');
$user = $userManager->findUserByConfirmationToken($token);
if (null === $user) {
/* ************************************
*
* User with token not found. Do whatever you want here
*
* e.g. redirect to login:
*
* return new RedirectResponse($this->container->get('router')->generate('fos_user_security_login'));
*
**************************************/
}
else{
// Token found. Letting the FOSUserBundle's action handle the confirmation
return parent::confirmAction($request, $token);
}
}
}

Resources