securing query authorization in linq-to-sql - c#

I have 3 tables: the User table, the Records table and the UserRecords table.
The columns are so:
UserTable
UserID | OtherUserDataFields
RecordsTable
RecordID | OtherRecordDataFields
UserRecords
UserID | RecordID
The UserRecords table tells me which users have authorization on which record. I have a function that updates the RecordsTable by receiving 2 paramaters: a record TheRecord (which contains a RecordID field) and a UserID.
I'm writing a query in the data context MyDC to fetch the record with the RecordID supplied in the parameter and test if the user is authorized on that record like this:
var RecordToUpdate = (
from r in MyDC.RecordsTable
from u in MyDC.UserRecords
where r.RecordID == TheRecord.RecordID && TheRecord.RecordID == u.RecordID
where u.UserID == TheUserID
select r).SingleOrDefault();
Will this ensure me that only records the user is authorized will be fetched? I want to avoid cases where users maliciously send a record they're not authorized on and make changes to these unauthorized records.
Thanks for your advice.

I agree with Anand, you will need a linq query:
var filterUserRecord = from u in MyDC.UserRecords
where u.UserID == TheUserID
select u;
var q1 = from r in MyDC.RecordsTable
where r.RecordID = TheRecordID
where filterUserRecord.Any(f => f.RecordID == r.RecordID)
select r;
This will be converted to the SQL query like the following:
SELECT * FROM RecordsTable rt WHERE rt.RecordID = TheRecordID AND EXISTS
(SELECT recordId FROM UserRecords ur WHERE ur.userId = TheUserID AND ur.recordID = rt.recordID)
Note that those are IQueryable<T>s and linq queries on it will produce another IQueryable<T> which will contain expressions to be wholly translated to SQL (or whatever the backend is) instead of naively evaluating it at client side.

Well I think your problem could be solved by subquery
sql:
select * from RecordsTable where recordId in
(select recordId from UserRecords where userId = #someUserId)
It could be represented in Linq as following
var filterUserRecord = from u in MyDC.UserRecords
where u.UserID == TheUserID
select u
var q1 = from r in MyDC.RecordsTable
where filterUserRecord.Any(f => f.RecordID == r.RecordID)
Details for subquery in Linq - Read from here

Perhaps read the article here which discusses how SQL Injection (which I assume should be the main security concern here) is handled in LINQ scenarios.
There is also a nice article here regarding Microsoft's security considerations for EF. It's worth a read for anyone developing with these tools!
[Edit] With regards to your last comment, you can use queries similar to those already on this page. To condense a little: If your database is normalised, to the extent that the RecordId is a unique primary key, you can bypass joins to make a query which reads a little better:
var targetRecords =
from userRecords in MyDC.UserRecords
where userRecords.UserTable.UserID == TheUserID
&& userRecords.RecordsTable.RecordID == TheRecord.RecordID
select userRecords;
var targetRecordsResult = targetRecords.SingleOrDefault();
I have separated the query from it's result 'var' here to point out that 'targetRecords' does NOT get evaluated until you call SingleOrDefault on it in order to assign to targetRecordsResult. You can of course wrap this into one statement if you wish.
If, as mentioned, your RecordID is a Unique Primary Key, you will either get the matching Record back, or null. Note that if this isn't the case, i.e. more than on Record could have the same ID, then the SingleOrDefault call can fail. If your database is designed like that, you would have to use a query more like the one which Anand has specified. That is slightly more verbose, but will return you ANY Record with a matching id for that specific user.
In terms of security, note that your SQL statement will get compiled containing the UserID, making it very difficult to tamper with. Hence my point that in this case, the scope and exposure of UserID is your main concern. If, like you have stated, the user (and any potential malitious user) has no access to the variable (via property exposure etc) then this should be more than suitable for your needs.

Try this:
var RecordToUpdate = (from u in MyDC.UserRecords
where u.UserTable.UserID == TheUserID
and u.RecordsTable.RecordID == TheRecord.RecordID).SingleOrDefault();
This will return the result for query which has specified UserID and RecordID.

Related

How to convert C# Linq to query in SQL Server?

I'm a junior developer and trying to convert the following linq statement to T-SQL:
var items = from u in DataContext.Users_SearchUsers(searchPara.UserFirstName,
searchPara.UserLastName,
searchPara.UserEmailAddress,
fetchOptions.page,
fetchOptions.rp,
fetchOptions.sortname,
fetchOptions.sortorder)
.ToList()
join a in DataContext.UserAccesses
.Where(x => x.Access.AccessTypeId == 4).ToList() on u.UserID equals a.UserId into accessGroup
select new {};
Can one please help me ? into accessGroup ---> (very important)
First of all you need to understand where your data is coming from. You are loading information from Users_SearchUsers on the one hand and UserAccesses on the other hand. The first query looks like
select <somecolumns>
from users
where <somefilters>;
(you need to use your actual columns and criteria, but Users_SearchUsers is not specified in the question at all). I have ignored paging here for the sake of simplicity
The second query looks like this:
select *
from user_accesses
where access_type_id = 4;
Let's join the two:
select <someoutercolumns>
from
(
select <someinnercolumns>
from users
where <somefilters>
) t1
join
(
select <someotherinnercolumns>
from user_accesses
where access_type_id = 4
) t2
on t1.user_id = t2.user_id;
These queries are probably not the exact solutions you need, but you want the answers to improve, then improve your question.
The requirement makes sense if the LINQ query is very slow. In that case you will need to refactor it in the following manner:
select <somecolumns>
from users
join user_accesses
on users.user_id = user_accesses.user_id and user_accesses.access_type_id = 4
where <somefilters>;
you can use this code
select *(you can put your columns instead *)
from Users
join UserAccesses
on Users.userid = UserAccesses.userid
where UserAccesses.typeid = 4;

Exclude a column from a select using LINQ

I'm developing a WCF RESTful web service with Entity Framework Code First.
I have a table Users with a lot of columns. I do this to get an specific user:
context.Configuration.ProxyCreationEnabled = false;
var users = from u in context.Users
where u.UserId == userId
select u;
On this table, there is a password column, and I don't want return this column.
How can I exclude password column from that select?
Its sad to say but NO
You do not have option to directly exclude any particular column. You may go with lazy loading of columns.
The easiest and non-liking method would be to include columns which you want.
Specify each column that you do want in your select statement:
var users = from u in context.Users
where u.UserId == userId
select u.UserId, u.Watever, etc... ;
another way like this,
var users = from u in context.Users
where u.UserId == userId
select new
{
col1 = u.UserId,
col2 = u.Watever
}.ToList();
You can create more than one LINQ object per table. I'd create one with the field you need and one without. It makes CRUD operations more difficult though.
Yes, you can run LINQ with certain columns excluded:
Store list of all columns in an array of string. E.g.
var arrayOfColNames = dbContext.Model.FindEntityType(typeof(TableName))
.GetProperties().Select(x => x.Relational().ColumnName).ToArray() )
Remove the unwanted columns from the above array of string. E.g.
arrayOfColNames = arrayOfColNames .Where(w => !w.Equals("password")).ToArray();
Run your LINQ select using the above filtered array of strings : https://stackoverflow.com/a/45205267/19386398

LINQ-to-SQL query not returning row when where clause compares against NULL value

Lets consider a table with 2 columns: ID (int) and Role(string). Both are nullable.
Now assume that the data in the two columns is:
ID Role
-- ----
1 NULL
2 Admin
The query looks like this:
List<types> t1 = (
from a in datacontext.RoleTable
where a.Role != "Admin"
select a
).ToList();
I thought the above query should be returning the first record of the table as its Role column is not equal to 'Admin' but the query returns an empty list.
Now when I use this query:
List<types> t2 = (
from a in datacontext.RoleType
where a.Role != "Admin" && a.Role == DBNull.Value.ToString()
select a
).ToList();
I get the correct answer.
Can anybody tell me why the first query is not working please.
FYI: If the Role column in the first row in the table is changed to User instead of NULL then the first query works fine.
I am using SQL Express and LINQ to SQL.
The first query doesn't behave as expected, because it is translated into SQL that is equivalent to the following:
select * from RoleTable where Role != 'Admin'
Now, in SQL NULL != 'Admin' is not TRUE (nor is it FALSE - it is undefined).
That's one of the many cases where the abstraction that LINQ to SQL provides is leaky and you still need to know SQL.
BTW: Your second query is also incorrect, it will select only those rows that are null. It wouldn't select a row with the role 'User'.
The correct query would look like this:
List<types> t2 =
(from a in datacontext.RoleTable
where a.Role != "Admin" || a.Role == null
select a).ToList();

LinQ to Entities: Query with five tables JOIN

I have five tables all with a primary key of ID
User
User_Role_Relation with Foreign Keys User_ID and Role_ID
Role
Role_Right_Relation with Foreign Keys Role_ID and Right_ID
Right
I am currently getting the Rights for the selected User with the following query in a stored procedure
SELECT DISTINCT
tbl_Right.ID, tbl_Right.Name
FROM
tbl_User_Role_Relation
INNER JOIN
tbl_Role_Right_Relation ON tbl_User_Role_Relation.Role_ID = tbl_Role_Right_Relation.Role_ID
INNER JOIN
tbl_Right ON tbl_Role_Right_Relation.Right_ID = tbl_Right.ID
WHERE
tbl_User_Role_Relation.User_ID = #User_ID
I am trying to covert this to LINQ to Entity with this code
var query = from r in context.Rights
from rrr in r.Role_Right_Relation
from rl in rrr.Role
from urr in rl.User_Role_Relation
where urr.User_ID == userid
select r;
but I get the following error
An expression of type 'Models.Role' is not allowed in a subsequent from clause in a query expression with source type 'System.Linq.IQueryable' Type inference failed in the call to 'SelectMany'
Any advise would be helpful.
Thanks.
First of all that linq query is doing a cross join, not inner join like your sql. You should check out this
Second of all, you would probably be best correctly defining the relationships between entities in the edmx and you probably won't have to join at all, instead you can use navigation properites to access parent/children and filter on those properties directly
The idea of the entity framework is you don't have to flatten the hierarchy
Edit 3
If you posted your code verbatim, then you are missing a line
from u in urr.Users
So:
var query = from r in context.Rights
from rrr in r.Role_Right_Relation
from rl in rrr.Role
from urr in rl.User_Role_Relation
from u in urr.Users
where u.User_ID == userid
select r;
Or at least there's a typo and the where should read:
where urr.User_ID == userid
resulting in:
var query = from r in context.Rights
from rrr in r.Role_Right_Relation
from rl in rrr.Role
from urr in rl.User_Role_Relation
where urr.User_ID == userid
select r;
If you use method syntax you should be able to do something like:
var user = context.Users.FirstOrDefault(u => u.Id == userId);
var rights =user.Roles.SelectMany(role => role.Rights).Distinct();
Be sure to check user is not null before getting rights

NHibernate: hql to criteria query - help needed

I have this hql query, which works perfect:
select m
from Media m
join m.Productlines p
join m.Categories c
join m.Spaces sp
join m.Solutions so
where m.Uid != 0
and p.Uid in (:productlines)
and c.Uid in (13)
and sp.Uid in (52)
and so.Uid in (15,18)
group by m.Uid
But now it needs to be parameterized/made dynamic, not only the parameters, but also the joins (it is possible to select only from Media, without any joins, and so no *.Uid in will be required in this case).
I dont want to mess around with a StringBuilder instance and build the hql query that way, I would rather like to use the Criteria API, but I cant get a
SELECT m.*
....
GROUP BY m.Uid
query to work with Criteria.
If I add a
Projections.GroupProperty("Uid")
to my query, nhibernate selects
SELECT m.Uid
....
GROUP BY m.Uid
which is of course wrong.
After that, I also need to count the unique rows the query returned, as the result is paged.
So, my other query is quite similiar, but I cant find a Criteria equivalent for
SELECT COUNT(DISTINCT m.Uid)
Here is the HQL:
select count(distinct m.Uid)
from Media m
join m.Productlines p
join m.Categories c
join m.Spaces sp
join m.Solutions so
where m.Uid != 0
and p.Uid in (:productlines)
and c.Uid in (13)
and sp.Uid in (52)
and so.Uid in (15,18)
How can this be done with Criteria API?
Please, (N)Hibernate experts - help me with this, I cant find a working solution. Any help is greatly appreciated!
Group columns are implicitly returned as result, but you can add more columns. AFAIK, you can return full entities:
var query = session.CreateCriteria(typeof(Media), "m")
.Add(Projections.GroupProperty("m"))
.Add(Restrictions.NotEq("m.Uid", 0));
// dynamically add filters
if (filterProductLines)
{
query
.CreateCriteria("m.Productlines", "p")
.Add(Restrictions.Eq("p.Uid", productLines));
}
// more dynamic filters of this kind follow here...
IList<Media> results = query.List<Media>();
To count the full number of results you can just build up the same query with different projection:
var query = session.CreateCriteria(typeof(Media), "m")
.SetProjection(Projections.CountDistinct("m.Uid"));
// rest of the query the same way as above
long totalNumberOfResults = query.UniqueResult<long>();
I'm getting unsure about the Projections.GroupProperty("m"), you need to try this. If it doesn't work, you could make it an DetachedQuery that only returns ids:
var subquery = DetachedCriteria.For(typeof(Media), "m")
.Add(Projections.GroupProperty("m.Uid"))
.Add(Restrictions.NotEq("m.Uid", 0));
// add filtering
var query = session.CreateCriteria(typeof(Media), "outer")
.Add(Subqueries.PropertyIn("outer.Uid", subquery));
IList<Media> results = query.List<Media>();
This creates a sql query like this:
select outer.* // all properties of Media to create an instance
from Media outer
where outer.Uid in (
select Uid
from media m
where // filter
)
var count = session.CreateCriteria(typeof(Media))
// Add other criterias...
.SetProjection(Projections.CountDistinct("Id")) // or whatever the id property of Media class is called
.UniqueResult<long>();
As to your GROUP BY question, the query:
SELECT m.*
....
GROUP BY m.Uid
makes no sense because you need to select only columns that appear in the group by clause or aggregate functions. Could you elaborate a little more as to what exactly are you trying to achieve?

Categories

Resources