How can I use group membership to restrict results? - asp.net-mvc

In an MVC application, for a given action that all users are authorized to perform, I would like to filter results based on the user's group membership. For instance ...
Users in GroupA should only see records pertaining to BuildingX.
Users in GroupB should only see records pertaining to BuildingY.
Users in GroupC should see all records.
I have no problem using authorization filters to restrict access to Actions, but I'm having a much harder time finding how to restrict access to data short of explicitly modifying statements every place where data is fetched.

Assuming your records are in a database, the roles membership model doesn't extend to the database out of the box. You can build a roles-based access control for your database, but you will likely save time using a simpler approach. For example, using code like this in your controller:
if (Roles.IsUserInRole("GroupA")) {
// Get data for GroupA.
}
// Display data...

A year later, working on a different but related issue, I found the EntityFramework.DynamicFilters package which does exactly what I need!

Related

Select specific fields from users and expanded manager

I want to get manager relation for all users and this can easily be done with https://graph.microsoft.com/beta/users?expand=manager.
But I get all data on all users and all data for each manager, which is way too much! I want to limit my result set to only return id and displayName for user and only id on the manager relation.
https://graph.microsoft.com/beta/users?select=id,displayName&expand=manager(select=id)
This is not working and I get this error:
Term 'manager($select=id)' is not valid in a $select or $expand expression.
Any help is much appreciated.
Sad to say, I was looking for this as well - it seems that this is not supported:
https://developer.microsoft.com/en-us/graph/docs/concepts/query_parameters#expand-parameter
Not all resources or relationships support using $select on expanded items.
I think this means you cant use select on manager expand.
I would take it further even and say you cannot use select on the users query itself, since there is no way to include the expanded "manager" in the select, even with all its properties. Once you set a select statement your expanded manager property will be gone.
There are two choices at this point:
get the users without their manager, then create batch requests of 20 at a time to get the users managers (id only)
give up on trying to be efficient, and just get all users with their managers with all their properties. This would bloat your requests but you will get away with fewer requests.
I think depending on how much data you are planning to get (number of users, properties you need) you should choose the best way forward for you.
Here is graph API documentation that shows how to expand the entire management chain and return selected fields. Slight caveat that it works on the documentation page but it doesn't return the expected results at https://developer.microsoft.com/en-us/graph/graph-explorer.
https://graph.microsoft.com/v1.0/me?$expand=manager($levels=max;$select=id,displayName)&$select=id,displayName&$count=true
Link to graph API User List Manager:
https://learn.microsoft.com/en-us/graph/api/user-list-manager?view=graph-rest-1.0&tabs=http#code-try-8

ASP.NET Model Id in ViewModel - is it safe?

Scenario:
(with an ASP.NET web app - Core or MVC)
I have a database with Users and Items for each user.
That means the UserId is a foreign key in the Items table.
From the browser I login as a User. I get my Items as a list of ItemViewModels, which are mapped (AutoMapper) to ItemViewModels via a simple api GET request.
I want to update one of the items (which should belong to me - the logged in user) via a simple API call. So I send the modified item back to the server via a PUT request as an ItemViewModel.
First approach:
The simplest approach would be to include the Item's database ID, ItemId, in the ItemViewModel - so when I receive the item to be updated as an ItemViewModel, I can map it back to the existing item in the database.
This however sounds pretty unsafe to me, as anyone could modify the PUT request with any ItemId and affect items which don't belong to the user who executed the request. Is there anything I'm missing about this approach?
Second approach:
Don't pass the database PK ItemId in the ItemViewModel.
Instead use an additional form of identification: let's say that user X has 10 items. And they are numbered from 1 to 10 using a property named UserItemId(which also exists in the database).
I can then pass this UserItemId in the ItemViewModel and when I get it back I can map it to an existing Item in the database (if all was ok with the request) or discard it and reject the request if the UserItemId didn't match anything from the logged in user's items.
Is anyone using this approach?
Pros:
The user only has access to it's own items and can't affect anyone else's since it doesn't know the actual Item ID (primary key), and any modifications are restricted to it's items.
Cons:
A great deal of extra management must be implemented on the server side for this approach to work.
Any other approaches ?
Please consider that the case mentioned above applies to all entities in the database which a client side implementation can CRUD, so it's not just the simple case described above.
The proposed solution should work for the entire app data.
I know this question has been asked here and here but the first one doesn't have a satisfying answer and I don't think the second one really applies to my situation, since it just deals with the UserId.
Thanks.
EDIT
Please consider the Item above as an aggregate root which contains multiple complex subItems each with a table in the db. And the question applies for them as much as for the main Item. That means that each subItem is passed as a ViewModel to the client.
I should mention that regarding further securing the update request:
For the first approach I can easily check if the user is allowed to change the item. But I should do this for all subItems too.
For the second approach I can check if the user can update the Item as follows: I get the userItemId of the incoming ViewModel -> I get all the logged in user's items from the database and try to find a match with the same userItemId, if I get a hit then I proceed with the update.
I think your application is not secure, if you only hide the Id.
You must check, before changing the database entity, if the user is allowed to change the entity.
In your case you should check, if your Id from the authenticated user is the UserId in your item.
If your ViewModel ist similar or identical for your API you could use a FilterAttribute in your controller.

Is it possible to set a scope to a domain class?

I'd like to know if it's possible with grails to specify a scope for the domain classes.
Few words to explain how my application is working at the moment:
- database access is done through an external "module" using SQLJ. This module is user by controllers in my grails app.
- a user ask for specific information submitting forms -> request submitted to the external module -> information extracted from the database -> information loaded into grails mem DB (HSQL) -> information displayed in views.
It works fine in development environment as i'm the only one using the application. But i'm wondering how the application would behave with two or more users. I mean, do the information loaded into grails memory database will be shared between users or not? And how not to shared information requested by one user with the others?
Thanks in advance for any help about this subject.
Regards.
All data in the database is shared across all users of the grails application. You would have to write a custom query to limit the data returned to a specific user. Based on your application maybe something similar to the following.
class DomainClass1 {
//fields you get from SQLJ go here
int userId
}
To get data into an instance of your domain class.
def domInstance=new DomainClass1()
domInstance.loadFromSQLJ() //call the SQLJ module and put it's data in the domain class
domInstance.userId=5 //assign the user associated with this info
domInstance.save()
Then when you want to display info for the user with the userId 5
def domInstance2=DomainClass1.findByUserId(5)
//Do stuff with domInstance2
It will be shared between all users.
But it depends on you, as for any other database, there must be some criteria (db column) by which you can choose only information related to current user.
In our project, we overrode domain classes' get(), list() that take into account domain aggregate root (a User or whatever), and also check all the named queries.
This leaves off all the other means of accessing instances, like findBy*(), criteria, findWhere() (though you can also override the dynamic methods), or HQL, but anyway reduces the amount of security review by 80%.
Suddenly it turned out to be OK to use DomainClass.list() in scaffolding.

how do I efficiently implement SO-like favorite and voting feature?

I'm using ASP.NET MVC, SQL Server 2008 and Forms Authentication. Suppose I am building a question-answer site like SO.
Here is the issue I am running into:
Users can view almost all the questions when not logged in, like they can on SO. Question-fetching query etc is almost written.
Now, if a user is logged in, he should also be able to tell whether he has already voted or "favorited" this question, like on SO.
Do I go back and rewrite all my
queries to include userIdInt
parameter even when the user is
anonymous just to know this
information?
Or when a user logs
in, I store what all he has voted on
and keep track of that throughout
his session?
Both seem cumbersome, but 1) seems more efficient at least. Anybody know how SO does this or more efficient way?
I think it doesn't keep track of whether the user has voted, but it does seem to keep track of whether the user has "favorited" that question.
You could use multiple interfaces, one for anonymous access, and one for authenticated access, and perform different queries for each:
http://en.wikipedia.org/wiki/Interface_segregation_principle
// Just example code - not asp.net-mvc
interface IAnonymousReader
{
IEnumerable<Answer> GetAnswers(int page, int countPerPage);
}
interface IAuthenticatedReader
{
IEnumerable<AuthenticatedAnswer> GetAnswers(int page, int countPerPage,
int userId);
// An alternative here is to get userId from concrete class, and pass in ctor
}
This would require that you write multiple queries, or that you get back data that you just throw away. You can avoid code duplication by constructing your query programatically. You could avoid writing the queries to begin with by using ORM.
As for the data, you could normalize the data, so that voting information is in a separate table, but still bound to the answer. When you're doing the query for anonymous users, simply don't join/query that table.

Symfony Admin Generator in multi user setup (restricting records in LIST view)

I am using SF 1.2.9 to build a website. I want to use the admin generator to provide admin functionality for the object models I have used (specifically LIST, edit and delete).
I have read the Symfony docs (Chapter 14), but unless, I am very much mistaken, all examples I have come accross so far, seems to be written for a single user environment only. Meaning that the list of records returned to the user is essentially, ALL the records in that table. In a multiuser environment, this is irresposible at best, and potentially, a security threat. It is a necessary requirement to restrict the list of records returned to a user to only those that they own (i.e. created).
Suppose I have a table with (YML) schema like this:
foobar_dongle:
id: ~
title: varchar(255)
info: longvarchar
owner_id: ~
created_at: ~
where owner id is a FK into a user table.
Assume I generate an admin module like this:
symfony propel:generate-admin backend FoobarDongle --module=dongle
Question:
How do I modify the list of records returned to a user in the LIST part of the code generated by the admin generator? As I mentioned above, currently, (i.e. out of the box), the admin generator presents the user (rather naively, I feel), with the ENTIRE set of records for the model being administered. I need to be able to restrict that list somehow, so that I can only return records owned by that user.
This is what I am trying to find out how to do.
I would be most grateful to anyone who can show me how I can restrict the list of records returned when using the admin generator for administration of an object model. Ideally, I would like to be able to specify a custom method that has all the custom 'filtering' logic - but so long as I can restrict the LIST of records a user can see (in admin), to only the records that he is the owner of, that is all I want to be able to do.
If you only want to restrict the returned objects in one or two modules, do this:
Go to the actions.class.php file of your module. There should be no methods by default and the class should inherit from autoModuleNameActions you. Insert the following method:
protected function buildQuery()
{
$query = parent::buildQuery();
// do what ever you like with the query like
$query->andWhere('user_id = ?', $this->getUser()->getId());
return $query;
}
But this becomes unhandy if you do it for more modules. In this case I would advice to create a new admin generator theme.
And if you want to make the query depending on some custom parameter in the admin generator config file, then you have to extend this file. But is not just done with adding a new parameter. You can read this article how to do this.
If you want to know more about the auto generated classes, have a look at this class: cache/[app]/[env]/modules/auto[ModuleName]/actions/actions.class.php.
Edit after comments:
I think you looked at the wrong class. Look here: cache/[app]/[env]/modules/auto[ModuleName]/actions/actions.class.php.
I set up a Propel project to check it and the method that is interesting for you is:
protected function buildCriteria()
{
if (is_null($this->filters))
{
$this->filters = $this->configuration->getFilterForm($this->getFilters());
}
$criteria = $this->filters->buildCriteria($this->getFilters());
$this->addSortCriteria($criteria);
$event = $this->dispatcher->filter(new sfEvent($this, 'admin.build_criteria'), $criteria);
$criteria = $event->getReturnValue();
return $criteria;
}
I also posted the whole content of this class to pastebin. It is a lot, the function is in line 245. Even if you don't find this class, you should be able to override this method like this:
protected function buildCriteria()
{
$criteria = parent::buildCriteria();
// do something with it
return $criteria;
}
I don't know about these criteria objects, so I can't help you with that but I hope the other things help you.
You should use sfGuardPlugin to provide your login/user functionality - it includes user groups and permissions that can be assigned to users and/or groups.
Using security.yml you can then configure which permissions/credentials are required to access individual actions. IE: you can allow everyone to access the list/update/delete actions, but only people with the create permission to access the create page.
The docs for sfGuardPlugin are worth reading:
http://www.symfony-project.org/plugins/sfGuardPlugin
Plus this section from the jobeet tutorial covers sfGuard and also use of security.yml and credentials:
http://www.symfony-project.org/jobeet/1_2/Propel/en/13
And to round off, this page from the book is relevant too:
http://www.symfony-project.org/reference/1_2/en/08-Security (although not sure it covers anything that isn't in the page i linked from jobeet)

Resources