I mean, some code that has his own logic related to a specific twig template and a related logic in a controller INSIDE another page.
Something like a bar with specific data for a user. Name, State, Phone number and some services and
this logic included I want to include it into pages where I decide to. Just reusing it.
You can just render a controller that returns that data from your views or make a service which fetches the data and expose it to twig.
1. Controller Example
Controller
class UserDataController extends Controller
{
public function userDataAction()
{
$userData = // fetch user data....
return $this->render('user_data_fragment_template.html.twig', ['user_data' => $userData]);
}
}
Some template where you want to show that fragment
<div>{{ render(controller('YourBundle:UserDataController:userData')) }}</div>
2. Service Example
Data Provider Service
class UserDataProvider
{
public function __construct(...)
{
....
}
public function getUserData()
{
$userData = // fetch user data...
return $userData;
}
}
config.yml
// ...
twig:
globals:
user_data_provider: #your_user_data_provider_service_name
Some template where you want to show that fragment
<div>{% include 'user_data_fragment_template.html.twig' with { userData: user_data_provider.userData } only %}</div>
Related
I know the following razor code works for AD groups.
<AuthorizeView Roles="AD_Group1, AD_Group2">
</AuthorizeView>
However, I will need to grant the permission from a json file. In the json file, it defines,
{
"WindowsUserName1" : [ "My own group 1", "My own group 2" ],
"WindowsUserName2" : [ "My own group 2", "My own group 3" ],
....
}
How to let <AuthorizeView> work with the custom defined grouping?
You can define custom policy to create authorization rules for user groups :
Building custom requirement :
public class UserGroupsRequirement : IAuthorizationRequirement
{
public string[] Groups { get; }
public UserGroupsRequirement(string[] groups)
{
Groups = groups;
}
}
Create a handler for requirement. This needs to inherit from AuthorizationHandler<T> where Tis the requirement to be handled :
public class UserGroupsHandler : AuthorizationHandler<UserGroupsRequirement>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, UserGroupsRequirement requirement)
{
var username = context.User.Claims.FirstOrDefault(c=>c.Type==ClaimTypes.Name).Value;
var groups = requirement.Groups;
//read json file and find user's groups and check whether groups inlcude in required groups.
if (true)
{
context.Succeed(requirement);
}
return Task.CompletedTask;
}
}
Register the policy :
services.AddAuthorization(config =>
{
config.AddPolicy("UserInGroupsAdmin", policy =>
policy.Requirements.Add(new UserGroupsRequirement(new string[] { "group1"})));
});
services.AddSingleton<IAuthorizationHandler, UserGroupsHandler>();
And you can update the AuthorizeView component to use policy :
<AuthorizeView Policy="UserInGroupsAdmin">
<p>You can only see this if you're an admin or superuser.</p>
</AuthorizeView>
You did not mention whether you're using a Blazor WebAssembly or Blazor Server. This distinction is important, especially when Authentication is involved. However, I guess that you're using Blazor Server as it seems to me that you're using WindowsAuthentication, right?
The following are the steps to do it:
Create a class that derives from the the AuthenticationStateProvider, and override its GetAuthenticationStateAsyn method. In this method, you should read the content of your JSON file, do whatever verification you need to do, and then return Task<AuthenticationState>. The AuthenticationState constructor gets a ClaimsPrincipal object that should contains all the claims you may create for selected users.
The GetAuthenticationStateAsyn method is called by both the CascadingAuthenticationState component, and by the AutherizeRouteView, and both cascade the AutheticationState to child components. The AutherizeView has this property:
[CascadingParameter] private Task AuthenticationState { get; set; }
Which is defined in AutherizeViewCore, so you must wrap your UI with CascadingAuthenticationState component if you wish to get the AuthenticationState object. It is advisable to wrap the App Router Component with the CascadingAuthenticationState component, so that the AuthenticationState is entirely available through your app, like this:
<CascadingAuthenticationState>
<Router AppAssembly="typeof(Program).Assembly" Context="routeData">
</Router>
</CascadingAuthenticationState>
Are you going to use AutherizeView to enable a bar-like UI with Log in Log out user buttons a user name label and an icon ? If not, you should not use the AutherizeView component...
Hope this helps...
I'm working on security for an API. Some things that I check on each request are:
Is the user's IP address whitelisted for access
Is the user's account expired
Is the user's rate limit exceeded for the day?
It seems like I should use a security Voter, perhaps one for each of these things, and return VoterInterface::ACCESS_DENIED when an access check fails.
However, I want to provide a message to the user in the API response that provides some indication as to WHY their request was denied. I cannot do this with a security voter.
My current workaround is to listen to the kernel controller event, perform my access checks, and then throw an explicit AccessDeniedException with my specific message if the check fails.
Is that a good way to handle this shortcoming? Maybe there's a way to do this within the security voter that I'm overlooking?
I know this is an old post but I just had the same problem and found a solution that works like a charm.
PS: I'm using symfony 3.4.4
The idea is to pass the RequestStack to the voter (constructor injection) then get the session and add a message in the flashBag to be displayed after that.
The voter constructor:
use Symfony\Component\HttpFoundation\RequestStack;
class ContactVoter extends Voter {
private $requestStack;
function __construct(RequestStack $requestStack) {
$this->requestStack= $requestStack;
}
If you are not using autowire and want to pass it as an argument in the service.yml you can use arguments: [#request_stack]
Inside the voter, the function that decide the permissions:
if ( 'Your_Access_Denied_Condition') {
$this->requestStack->getCurrentRequest()->getSession()->getFlashBag()->add('danger', 'Your message !');
return false ;
}
The template to display the message
{% if app.request.hasPreviousSession %}
{% for type, messages in app.session.flashbag.all() %}
{% for message in messages %}
<div class="alert alert-{{ type }}">
{{ message|trans({}, 'messages') }}
</div>
{% endfor %}
{% endfor %}
{% endif %}
If you dont want to use the flashBag, you can use the same logic to throw a customized exception with a specific message, catch it with a listener and display the message you want.
Ok I've had the same problem as you did. There is no easy way for this one you'd have to overwrite the default SecurityListener of Symfony Sensio\Bundle\FrameworkExtraBundle\EventListener\SecurityListener with something in those lines:
use Sensio\Bundle\FrameworkExtraBundle\EventListener\SecurityListener;
class MySecurityListener extends SecurityListener
{
public function onKernelController(FilterControllerEvent $event)
{
$request = $event->getRequest();
if (!$configuration = $request->attributes->get('_acme_security')) {
return;
}
// trick to simulate one security configuration (all in one class/method).
$request->attributes->set('_security', new SecurityConfiguration($configuration));
if (!$this->language->evaluate($configuration->getExpression(), $this->getVariables($request))) {
throw new AccessDeniedException(sprintf($configuration->getMessage());
}
parent::onKernelController($event);
}
}
On top of that you'll need to extend Sensio\Bundle\FrameworkExtraBundle\Configuration\Security like so:
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security as SensioSecurity;
/**
* #Annotation
*/
class Security extends SensioSecurity
{
protected $message;
public function getAliasName()
{
return 'acme_security';
}
public function getMessage()
{
return $this->message;
}
}
This above will allow you to add a message property to your controller annotation and that will be used if there is AccessDenied exception.
And here how to configure your security listener in Yaml:
acme.security.listener:
class: AppBundle\EventListener\SecurityListener
parent: sensio_framework_extra.security.listener
tags:
- { name: kernel.event_subscriber }
I want to know the best approach to create my controllers structure.
Let's say I have several events and for each event I can have several devices.
My idea would be to have something like:
http://mydomain/event/1/device/4
So I can access the deviceId 4 (belonging to eventId 1).
Should I have two different controllers? One for Event and for Device or device info has to be in EventController?
How can I have this routing in my RouteConfig?
It's entirely up to you how you want to set this up. You can use separate controllers or the same controller. It doesn't matter.
As far as routing goes, if you're using standard MVC routing, you'll need to create a custom route for this:
routes.MapRoute(
"EventDevice",
"event/{eventId}/device/{deviceId}",
new { controller = "Event", action = "Device" }
);
Which would correspond with something like this:
public class EventController : Controller
{
public ActionResult Device(int eventId, int deviceId)
{
...
}
}
Just make sure you place that before the default route, so it will catch first. For more about custom routes see: http://www.asp.net/mvc/overview/older-versions-1/controllers-and-routing/creating-custom-routes-cs
Alternatively, in MVC5+ you can use attribute routing, which makes defining custom routes much easier if you're doing a lot of stuff like this. In RouteConfig.cs, uncomment the line:
// routes.MapMvcAttributeRoutes();
Then, on your action define the route like:
[Route("event/{eventId}/device/{deviceId}")]
public ActionResult Device(int eventId, int deviceId)
{
...
You can also use [RoutePrefix] on your controller class to move part of the route to apply to the whole controller. For example:
[RoutePrefix("event")]
public class EventController : Controller
{
[Route("{eventId}/device/{deviceId}")]
public ActionResult Device(int eventId, int deviceId)
{
...
}
}
For more about attribute routing see: http://blogs.msdn.com/b/webdev/archive/2013/10/17/attribute-routing-in-asp-net-mvc-5.aspx
I've a service registered for Twig and i use its method in my main layout.twig.html to list some things.
Next, in some actions i use the same service to change its state (change some private fields there) and i would like to see those changes in my rendered page. But it looks like Twig invokes the "getter" method to soon, when my data is not yet managed by controller's action.
What is the best practice for such case? Should i somehow use some Events and make my Service kind of event listener?
Example layout code:
<div>{{ myservice.mymethod() }}</div>
Service:
class MyService {
private $myfield = null;
....
public function setMyField($value) {
$this->myfield = $value;
}
public function myMethod() {
if($this->myfield === null) {
return 'not initialized';
} else {
$this->myfield;
}
}
....
Some controller action:
$myservice = $this->container->get('myservice');
$myservice->setMyField('setted in action');
And i always get not initialized on rendered page
I think you have to register this service as a twig extension.
check out this manual: http://symfony.com/doc/current/cookbook/templating/twig_extension.html.
I am aware simple role provider in which if i need to restrict particular action i have to simply write Authorize(Roles = "Admin") or if i need to restrict particular part of view i nned to write #if(User.IsInRole("Admin")).
But my question is that what if my roles are not fixed and it is stored in database and my super admin can able to edit and delete them.
My requirement is that superadmin can add,update,delete roles and also create different users and maintain the roles of those users.
I have done lot of googling and found something as follows
[AttributeUsage (AttributeTargets.Method|AttributeTargets.Class,Inherited = true,AllowMultiple=true) ]
public class CustomRole : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase context)
{
Respository db = new Respository();
if (db.UserMasters.Where(x => x.user_name == context.User.Identity.Name).Count() > 0)
{
return true;
}
else { return false; }
}
}
Here i can use this code to authorize action method as follows
[CustomRole]
public ActionResult Details(int id = 0)
{
Employee employee = db.Employees.Find(id);
if (employee == null)
{
return HttpNotFound();
}
return View(employee);
}
Here my this action method is protected but what if i want to protect some part of view by this custom method. How to use this functionality to achieve functionality as User.IsInRole("Admin")?
your requirement will get in 3 steps
1- Create all default roles, store it in database.i.e- roleid,rolename
2- When creating new user map userid with roleid.
3- also make one table for all permission which you have to give.
4- make seperate ui for admin to change the roles of each user.
database will be like below image.
and ui will be like this.
try this yousrelf..
Fully answering your question might be out of scope for StackOverflow, since it would basically require writing most of an application for you, but here's the general idea.
Write a helper class similar to this:
public class ModuleHelper
{
public static bool UserCanAccessModule(string moduleIdentifier)
{
bool canAccess = false;
/*
Call into your service with current User.Name and module identifier (integer, GUID, whatever).
Return result of whether user has the required role for the specified module
*/
try
{
canAccess = service.CanUserAccessModule(User.Identity.Name, moduleIdentifier);
}
catch
{
// catching all exceptions, since this is a UI helper
}
return canAccess;
}
// etcetera...
}
I'd suggest wrapping it in the root namespace of your application; otherwise, add a reference to this class's namespace in the system.web.webPages.razor section of the web.config in the Views folder. Then, you can do something like:
<div class="col-sm-3 col-md-2 sidebar">
#if (ModuleHelper.UserCanAccessModule("moduleXYZ"))
{
#Html.Action("moduleXYZ")
}
</div>
This obviously assumes a lot, but the idea isn't new or all that complicated in practice. The logic of the service is relatively simple:
Look up the user
Look up the "action" or "module"
Look for intersection (if any) between the roles assigned to each.
No intersection means user doesn't have the required role.
Tieson T. has a great answer to your question already, so what I'll provide here is an alternative method if you wanted to keep all of your authorization steps all in controllers.
Consider separating the different aspects (or restricted parts) of your main view into a partial view (or views) that perform the restricted functionality. Then, instead of using: #Html.RenderPartial("ViewName", Model) you can set up your partials to be returned from controller actions decorated with the ChildActionOnly Attribute by using the RenderAction Html Helper.
For example:
<div class="col-sm-3 col-md-2 sidebar">
#Html.RenderAction("RestrictedContent")
</div>
Then in your controller class
public class RestrictedController : Controller {
public RestrictedController() : base() {
}
[ChildActionOnly()]
[CustomRole()]
public ActionResult RestrictedContent() {
return PartialView("RestrictedPartial");
} // end action RestrictedContent
} // end class
The only consideration with this approach will be in your custom attribute to interrogate the the IsChildAction property to avoid rendering a redirect or whatever your attribute does in the case the user is not authorized since you'll probably want to just not render anything.
For Example (in your custom attribute class):
public override void OnAuthorization(AuthorizationContext filterContext) {
if(filterContext.IsChildAction) {
filterContext.Result = new EmptyResult(); // return an empty result instead of performing a redirect.
} else {
base.OnAuthorization(filterContext); // continue with custom authorization if it is not a child action
} // end if/else
} // end method OnAuthorization
Phil Haack has an article describing the usage of the RenderAction method here: http://haacked.com/archive/2009/11/18/aspnetmvc2-render-action.aspx/
Also, see here for an interesting discussion on the differences between Action and RenderAction. The difference between Html.Action and Html.RenderAction