I've seen one article that said groups were bad to use if you needed anything to be secure, but I'm wondering if that is still true or how someone would take advantage of this scenario:
Say I had one company with multiple subdivisions. I want to send notifications only to a particular subdivision when an action happens. Even though it's odd, say in this scenario that it would be bad security wise if a different subdivision saw the notification.
The client hub does not have any "send" methods on it so it looks like this:
[Authorize]
public class NotificationsHub : Hub
{
}
I then have a service class on the api that uses the IHubContext to send a notification to the group
public class NotificationService : INotificationService{
public async Task UpdateScheduleAsync(string subdivision, string message)
{
await _hubContext.Clients.Group(subdivision).SendAsync("ReceiveMessage",message);
}
}
If I added in the NotificationsHub the method:
public override async Task OnConnectedAsync()
{
//may or may not use the identifier
var someUserIdentityInfo = Context.UserIdentifier;
//would grab the subdivision the authorized user is apart of
var subdivision = GrabSubdivisonFromIdentity(someUserIdentityInfo);
//add to the subdivison group
foreach(var division in subdivison){
await Groups.AddToGroupAsync(Context.ConnectionId, division);
}
await base.OnConnectedAsync();
}
Is there any way for a person to connect to the group if they know the other division names? Is this still a bad idea?
I'm trying to avoid keeping a list of "logged in" users and sending out the change by user individually one by one for each change.
Related
I have two projects
WebApp(SpringMVC)
Microservice
The idea is that I have a page in which I list all the users from DB, so basically I need a listener on WebApp side and a producer in the microservice side, typically the flow is as follow
Whitout rabbitmq(synchronous)
Click page "List Users"
UserController that redirect me to a specific service
public List<User>getUsers(){//no args!!
service.getUsers();//no args
}
UserService with logic to access DB and retrieve all users
public List<User>getUsers(){//no args!!
//connect to DB and retrieve all users
return users
}
Render users on jsp
With RabbitMQ and assuming users has already been produced the list of users on the microservice's side
My question is about if I introduce rabbitmq then I need a method in which I listen a message(List of products as a JSON) but now the flow change a little bit comparing with the first one because
Click button "List Users"
Controller need a method findAll(Message message), here I need pass a message because the service is expecting one as the service is Listener
public List<User>getUsers(Message message){
service.getAllUsers(**String message**);
}
The service as right now is listen a message, I need to pass a Message
arg in which I will be listen the queues
#RabbitListener(queues = "${queue}", containerFactory = "fac")
public List<User> getUsers(String message){
//Transform JSON to POJO
//some logic...
return users;
}
So basically my question is the second flow is correct?
If so how I have to pass the Message object from controller to service
because in controller I do not needed a Message, but in order to
listen I have, is this correct?
If so how pass the message arg
There is a better way to achieve this?
Regards
Use a Jackson2JsonMessageConverter and the framework will do all the conversion for you.
Controller:
List<User> users = template.convertSendAndReceiveAsType("someExchange", "someRoutingKey",
myRequestPojo, new ParameterizedTypeReference<List<User>>() {});
Server:
#RabbitListener(queues = "${queue}", containerFactory = "fac")
public List<User> getUsers(SomeRequestPojo request){
//some logic...
return users;
}
Wire the converter into both the listener container factory and the RabbitTemplate.
Request Pojo and Users have to be Jackson-friendly (no-arg constructor, setters).
Just trying to wrap my mind around handling pushing data to some users through SignalR.
Say, in a private chat situation, Person A sends a message to Person B. If I'm understanding the concept correctly, I need to create a Group for each user who logged into the system. This way, when the new message from Person A comes in, in my SignalR Hub, I'd send the message to the group for Person B which only has only one user in it i.e. Person B.
In this approach, I'd essentially create a unique Group for each user so that I have the flexibility to push data to each unique user. I could use the UserId as the Group Id.
Is this the way to handle pushing data selectively to each unique user?
You can grab client Ids manully like so and then send them using the hubcontext to avoid creating a lot of groups (you just need to implement a menthod which get you're connections from your resource in my case im using dtos)
I'm assuming your hubs have some type of connection manager, here's a sample of one from the docs
In my case I have a dynamic configuration which I use to configure my hubs using the Type of DTO, and I send hubs using a generic hub base here is a sample implementation:
Warning: things may slightly be different I'm using .Net-Core in this example
//NOTE: THub must be the one Registered with MapHubs in the Startup
//class or your connections won't be there because Hubs are not contravarient
public abstract class HubBase<TDTO, THub> : Hub
Where THub: HubBase<TDTO, THub>
{
protected IHubContext<THub> hubContext;
HubBase(IHubContext<THub> hubContext)
{
this._hubContext = hubContext;
}
protected abstract List<string> GetConnectionsByDto(TDTO dto)
protected async Task SendFilteredAsync(string eventName, TDTO dto)
{
var clientIds = await this.GetConnectionsByDto(dto);
if (false == clientIds.Any())
{
return;
}
var broadcastTasks = clientIds.Select(async clientId =>
await this._hubContext.Clients.Client(clientId).SendAsync(eventName, dto));
await Task.WhenAll(broadcastTasks);
}
}
I'm looking into asp.net core and the new security policies and claims functionality. Having just looked at it I don't see how it is much better than the existing authorize attribute logic in the past where hard-coded roles or users are decorated on controllers, methods etc. To me the issues has just been moved from hard-coding in attributes to hard-coding policies.
Ideally I would like to perform activity/resource based authorization where everything would be database driven. Each activity or resource would be stored in the database and a permission/role would be assigned to the resource.
While researching the topic I found this fantastic article by Stefan Wloch that pretty much covers exactly what I'm looking to do.
http://www.codeproject.com/Articles/1079552/Custom-Roles-Based-Access-Control-RBAC-in-ASP-NE
So my question is with the new core features how does it prevent us from having to hard-code and recompile when the time comes to change what roles/permissions are allowed to access a controller or method in a controller? I understand how claims can be used to store anything but the policy portion seems susceptible to change, which gets us back to square one. Don't get me wrong, loving asp.net core and all the great changes, just looking for more information on how to handle authorization.
There are at least 2 things that need to be consider in implementing what you want. The first one is how to model the Controller-Action access in database, the second one is to apply that setting in asp.net core Identity.
The first one, there are too many possibilities depend on the application itself, so lets create a Service interface named IActivityAccessService that encapsulate. We use that service via dependency injection so that anything that we need can be injected to it.
As for the second one, it can be achieved by customize AuthorizationHandler in a policy-based authorization. The first step is to setup things in Startup.ConfigureServices :
services.AddAuthorization(options =>
{
options.AddPolicy("ActivityAccess", policy => policy.Requirements.Add( new ActivityAccessRequirement() ));
});
services.AddScoped<IAuthorizationHandler, ActivityAccessHandler>();
//inject the service also
services.AddScoped<IActivityAccessService, ActivityAccessService>();
//code below will be explained later
services.AddHttpContextAccessor();
next we create the ActivityAccessHandler:
public class ActivityAccessHandler : AuthorizationHandler<ActivityAccessRequirement>
{
readonly IActivityAccessService _ActivityAccessService;
public ActivityAccessHandler (IActivityAccessService r)
{
_ActivityAccessService = r;
}
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext authHandlerContext, ActivityAccessRequirement requirement)
{
if (context.Resource is AuthorizationFilterContext filterContext)
{
var area = (filterContext.RouteData.Values["area"] as string)?.ToLower();
var controller = (filterContext.RouteData.Values["controller"] as string)?.ToLower();
var action = (filterContext.RouteData.Values["action"] as string)?.ToLower();
var id = (filterContext.RouteData.Values["id"] as string)?.ToLower();
if (_ActivityAccessService.IsAuthorize(area, controller, action, id))
{
context.Succeed(requirement);
}
}
}
}
public class ActivityAccessRequirement : IAuthorizationRequirement
{
//since we handle the authorization in our service, we can leave this empty
}
Since we can use dependency injection in AuthorizationHandler, it is here that we inject the IActivityAccessService.
Now that we have access to what resource is being requested, we need to know who is requesting it. This can be done by injecting IHttpContextAccessor. Thus services.AddHttpContextAccessor() is added in code above, it is for this reason.
And for the IActivityAccessService, you could do something like:
public class ActivityAccessService : IActivityAccessService
{
readonly AppDbContext _context;
readonly IConfiguration _config;
readonly IHttpContextAccessor _accessor;
readonly UserManager<AppUser> _userManager;
public class ActivityAccessService(AppDbContext d, IConfiguration c, IHttpContextAccessor a, UserManager<AppUser> u)
{
_context = d;
_config = c;
_accessor = a;
_userManager = u;
}
public bool IsAuthorize(string area, string controller, string action, string id)
{
//get the user object from the ClaimPrincipals
var appUser = await _userManager.GetUserAsync(_accessor.HttpContext.User);
//get user roles if necessary
var userRoles = await _userManager.GetRolesAsync(appUser);
// all of needed data are available now, do the logic of authorization
return result;
}
}
Please note that the code in IsAuthorize body above is an example. While it will works, people might say it's not a good practice. But since IActivityAccessService is just a common simple service class, we can inject anything that wee need to it and modify the IsAuthorize method signature in any way that we want to. For example, we can just pass the filterContext.RouteData instead.
As for how to apply this to a controller or action:
[Authorize(Policy = "ActivityAccess")]
public ActionResult<IActionResult> GetResource(int resourceId)
{
return Resource;
}
hope this helps
I want to make a service that notify the user in case there are some new messages sent to him. Thus I want to use some Comet framework that provide the server push ability. So I have looked into PokeIn.
Just wondering a thing. I have checked on the samples that they have on the website. None of them look into the database to retrieve new entries if there are some. But it is just a matter of modification to it I guess.
One of the sample implement this long polling by using a sleep on the server side. So if I use the same approach I can check the database, if there are any new entries, every 5 seconds. However this approach doesn't seem to be much different from when using polling on the client side with javascript.
This part is from a sample. As can be seen they put a sleep there for to update current time for everybody.
static void UpdateClients()
{
while (true)
{
//.. code to check database
if (CometWorker.ActiveClientCount > 0)
{
CometWorker.SendToAll(JSON.Method("UpdateTime", DateTime.Now));
}
Thread.Sleep(500);
}
}
So I wonder is this how I should implement the message notifier? It seems that the above approach is still going to push a huge load demand on the server side. The message notifier is intend to work same way as the one found Facebook.
You shouldn't implement this way, that sample is only implemented like that because the keep PokeIn related part is clear. You should implement SQL part as mentioned http://www.codeproject.com/Articles/12335/Using-SqlDependency-for-data-change-events
in order to track changes on database.
So, when you have something to send, call one of the PokeIn methods for the client side delivery. I don't know, how much your application is time critical because in addition to reverse ajax, PokeIn's internal websocket feature is very easy to activate and delivers messages to client quite fast.
You can do this with database as #Zuuum said, but I implemented it in a different way.
I'm using ASP.NET MVC with PokeIn and EF in a Windows Azure environment:
I have domain events similar to this approach: Strengthening your domain: Domain Events
When someone invokes an action, that's a Unit of Work
If that UOW succeeds then I raise a domain event (e.g. ChatMessageSent)
I have subscribers for these domain events so they can receive the event and forward the message to the PokeIn listeners
I use this pattern for all my real-time needs on my game site (making moves, actions etc in a game), I don't want to advertise it here, you can find it through me if you want.
I always use this pattern as a duplex communication solution so everybody gets their update via PokeIn, even the user who invoked the action so every client will behave the same. So when someone calls an action it won't return anything except the success signal.
The next examples are won't work because they are only snippets to demonstrate the flow
Here is an action snippet from my code:
[HttpPost]
[UnitOfWork]
[RestrictToAjax]
[ValidateAntiForgeryToken]
public JsonResult Post(SendMessageViewModel msg)
{
if (ModelState.IsValid)
{
var chatMessage = new ChatMessage
{
ContainerType = msg.ContainerType,
ContainerID = msg.ContainerID,
Message = _xssEncoder.Encode(msg.Message),
User = _profileService.CurrentUser
};
_chatRepository.AddMessage(chatMessage);
OnSuccessfulUoW = () => EventBroker.Current.Send(this, new ChatMessageSentPayload(chatMessage));
}
return Json(Constants.AjaxOk);
}
And the (simplified) EventBroker implementation:
public class UnityEventBroker : EventBroker
{
private readonly IUnityContainer _container;
public UnityEventBroker(IUnityContainer container)
{
_container = container;
}
public override void Send<TPayload>(object sender, TPayload payload)
{
var subscribers = _container.ResolveAll<IEventSubscriber<TPayload>>();
if (subscribers == null) return;
foreach (var subscriber in subscribers)
{
subscriber.Receive(sender, payload);
}
}
}
And the even more simplified subscriber:
public class ChatMessageSentSubscriber : IEventSubscriber<ChatMessageSentPayload>
{
public void Receive(object sender, ChatMessageSentPayload payload)
{
var message = payload.Message;
var content = SiteContent.Global;
var clients = Client.GetClients(c => c.ContentID == message.ContainerID && c.Content == content)
.Select(c => c.ClientID)
.ToArray();
var dto = ObjectMapper.Current.Map<ChatMessage, ChatMessageSentDto>(message);
var json = PokeIn.JSON.Method("pokein", dto);
CometWorker.SendToClients(clients, json);
}
}
I want to use signalR for my clint browser website so it can receive messages from the server when a new order is added. So I want it to react to a server side event that is not triggered by any browser.
There are multiple users on the website. A user should be notified when there is a new order placed for him on he server. How an I notify only a specific user, and do this from the method that adds the user?
Is there any code like:
var chat=new Chat();
chat.Send("hihi");
placed in AddOrder method, with
public class Chat : Hub
{
public void Send(string message)
{
// Call the addMessage method on all clients
Clients.refresh(message);
}
}
You can override the default client id (used to identify the users browser window) and replace with your own. Your client id would come from your membership provider.
Create a new class and implement IConnectionIdGenerator.
public class UserIdClientIdFactory : IConnectionIdGenerator
{
public string GenerateConnectionId(IRequest request)
{
return Guid.NewGuid().ToString();
}
}
The method above just creates a new Guid, but you would return the customers id from your membership provider.
You then need to register this new class with SignalR dependencyresolver, so in the Application_Start method in the global.asax file add the following line
GlobalHost.DependencyResolver.Register(typeof(IConnectionIdGenerator),
() => new UserIdClientIdFactory());
When a new order is placed you would then get the specific client and broadcast a message to them, for example:
//clientId matches the user id from you membership provider.
var clients = GlobalHost.ConnectionManager.GetHubContext().Clients;
clients[clientId].yourClientSideCallBackMethodGoesHere(someValue);
You have to store the Context.ConnectionId for all connected users, tie that to your website users and then use Clients[connectionId].addMessage(data);
One way you can do this is to hold a collection of Users (website users) each paired to a connection Id. You can then use SignalR events OnConnected / OnDisconnected to pop users in and out of this list.
E.g.
public override Task OnConnected()
{
// Add users here with Context.ConnectionId
}
public override Task OnDisconnected()
{
// Remove users from collection here by identifying them with Context.ConnectionId
}