I'm making a security permission system for a Dynamic Data site based on the article Securing Dynamic Data Preview 4 Refresh. The system contains an additional permission kind: "deny an operation for a record/field if a record is not owned by an user".
If an user can read only own objects, we need to have an always enabled filter in List and check permissions in Details. If an user can write only own objects, we need to check permissions in Edit and Delete, remove "Edit/Delete" links from some rows in List, make "User" field readonly and provide its value in Insert. I didn't think about column-level permissions of this kind yet.
So, the main problem, as I see at this moment: too many places to place the same checks (I didn't even think of malicious user crafting POST data). Also I couldn't make make a field in Insert at the same readonly and having a value which is displayed and saved to DB (I don't want to place that in the model partial classes because I think that there are already too many places that need to be edited to implement this functionality).
Is there a single place to deny a read or write operation with an object depending on the object value?
How can I provide a default value to the field, so that it will be shown on the Insert page, inserted to the DB and couldn't be changed by the user before inserting?
The following assumes you're using LINQ to SQL.
Is there a single place to deny a read
or write operation with an object
depending on the object value?
Reads
I know of no simpler way to restrict reads than to add a filter to all the relevant LinqDataSource controls. If you are able to implement your filter generally, you can write one QueryCreated handler, then add a single line registering your custom handler to all the page templates.
Writes
In the Dynamic Data metadata, add an OnValidate partial method to all the relevant tables. If the current user is not allowed to ChangeAction the given record, throw an exception. You will still have to update all the page templates to hide UI elements that the user does not have access to, but at least you can rest assured that the worst that could happen in some unexpected case is that the user sees an error page.
How can I provide a default value to
the field, so that it will be shown on
the Insert page, inserted to the DB
and couldn't be changed by the user
before inserting?
Perhaps look at some combination of adding an OnCreated and OnValidate partial methods. See also: this answer.
Related
In an MVC application I have a two pages process. On the first page we gather information that will allow us to identify which database record to update. On the second page we gather new values used to update this record. In order for this to work, we need a way to persists information between the two pages, including some record id.
I though of two way to do this and both have some problem.
Store the information in the Session object.
This works as long as the user does not open a second browser window or tab. If he does there is a risk that he'll apply the modifications to the wrong record. Suppose he opens tab 1 and complete the first step. Record id 1 is stored in the Session object. The user then open tab 2 and complete the first step. Record id 2 is then stored in the Session object overwriting record id 1. The user then come back to the first tab and complete the second step thinking he is editing record 1, but in fact he will be editing record 2.
Store the information in an hidden field on the page.
This would solve the problem solution 1 has, but it would be trivial for a ill-intentioned user to change the record id to overwrite any record.
While typing this question I just though of a third solution. That is an hybrid of theses two, but I'm not sure it's completely safe. We could store a random id in an hidden field on the page and use this to prefix the key we use to access data in the session object. I think this would work. Could this be exploited as solution 2 could?
Any other good way to securely store data "per tab" instead of "per session"?
Considering way 2 you may check security server side. If a user does not have modification rights on a specific record then server must not save it. Otherwise he/she is modifying a record that has modifications rights on it and does not matter if he/she is doing it by standard UI or hacking under it.
I think you are mixing up two things - authorization and passing data.
If user is authorized to do stuff with "another record", it's not important if he "tempers the hidden", because he is authorized to change another record as well. Nobody is going to do that intentionally. Means - you just need to check if user is authorized to do stuff in every post from the user i.e. in each controller method (and this is normal practice to always validate all user input server-side).
I would suggest you go with "hidden field".
If you want to separate info in different tabs you should use sessionStorage that differs for each open browser tab.
You can set it like this:
sessionStorage.setItem("perTabValue", "true");
Then you can get your value:
var x = sessionStorage.getItem("perTabValue");
if(x === "yourValue"){
//do anithing you want
}
Users modify a DB object in an edit form that I have, pretty straight forward.
I need to implement a 'change log' on this object. I need to record which fields where changed and what they were before and after. I'm using Razor MVC.
I've done this by writing triggers for the table on update/delete. On update/delete of a record, the trigger pushes the record to a History table, in a History database. This creates the change log. Then you would just need to display it; to identify the change would require evaluating each and every field.
There's nothing already built that wold do this for you that I know of.
We created special form to creating purchase prices for vendors.
New form has almost the same fields as original (so we used PriceDiscTable), but the record/datasoruce was set as temporary table. After user filled mandatory fields will click button, (extra logic behind) and record will inster to database (real priceDiscTable).
The idea was to grand access to trade prices for users that not necessarily has access to purchase prices. In theory everything was ok, but when user with no access to PriceDiscTable open new form, error was shown "Not enougt right to use table 'Price agreements'".
We try set the AllowCheck to false in formDatasource but this only allow us to open the form, but user still cannot add or modify records.
Is there any way to force system to allow user to write data in the temporary table?
Disabling security key or grand access to real table is not an option.
Duplicate table and create with same fields is nuisance (if we use same table we can use data() method to assign fields)
I think that creating a new temporary table with [almost] the same fields would be the best solution.
If the only reason you oppose to this approach is that you wouldn't be able to use data() to copy data from one table to another you can use buf2BufByName() as described here: http://mybhat.blogspot.co.uk/2012/07/dynamics-ax-buf2buf-and-buf2bufbyname.html
You can use RunAs to impersonate another user...perhaps a system user. I don't entirely follow what you are trying to do, but it sounds like this solution would work for you if you know exactly what your custom code is doing and is capable of.
See Classes\AifOutboundProcessingService\runAsWrapper to see an example.
You will not be able to display the PriceDiscTable without giving the user at least "view" access or editing Classes\FormRun to somehow bypass the security key, which is kernel level so it's also not possible.
I agree with 10p where you should create a temp table and then create a custom method handler combined with buf2bufbyname() or buf2buf().
Another option you can screw around with, if you REALLY want to use .data() is using a Common as the datasource. You could add the fields you want on the grid with the common, then you can pass a common back/forth. This has a good amount of form setup to get this working, but it could produce what you want eventually I think.
static void Job8(Args _args)
{
Common common;
salesTable salesTable;
;
common = new DictTable(366).makeRecord();
select firstonly common where common.RecId == 5637145357;
salesTable.data(common);
info(strfmt("%1 - %2", salesTable.SalesId, salesTable.SalesName));
}
I have an .aspx form with 20 fields that must be disable based on a users role and a status of a order record. Currently the application has 5 roles and 3 status, so I have 300 different possible conditions that I have to account for.
My first thought is to store each permutation in a table, then set the fields when the page loads by looping through the fields. Is there a better way? Please note, I am using .Net 2.0 and NOT MVC.
I'd probably store the details of each field, and then the roles and status that can edit them, and do it that way.
What are the rules for the system? Basically, are there really 300 possible conditions? Or is that really certain fields are only editable for certain status, and then only certain roles can edit those fields? Or is it that certain fields are available for certain roles as well?
If it's more of the former I'd probably have something like this:
Three primary tables (makes it easy to extend if you add a field, role or status):
Fields
Roles
Status
Then two link tables:
Field.Id and Role.Id
Field.Id and Status.Id
Then for any given order and user you can then find which Fields are editable for the order's current status, and the users role, and as you work through the fields set the access rights appropriately - however you set the controls - either dynamically generating them based on the collection you get back, or statically on the page.
If you have an issue where the Role can override the Status, you could also store a boolean in the Field/Role table, indicating whether the Field should be avaiable regardless of status.
Like another responder, we also use a Business Object framework called CSLA. CSLA implements field-level security checks by requiring class developers to do security checks in the property get/set calls. A typical property implementation looks like this:
Private mFirstName As String = ""
Public Property FirstName() As String
<System.Runtime.CompilerServices.MethodImpl(Runtime.CompilerServices.MethodImplOptions.NoInlining)> _
Get
CanReadProperty("FirstName", True)
Return mFirstName
End Get
<System.Runtime.CompilerServices.MethodImpl(Runtime.CompilerServices.MethodImplOptions.NoInlining)> _
Set(ByVal value As String)
CanWriteProperty("FirstName", True)
If value Is Nothing Then value = ""
If Not mFirstName.Equals(value) Then
mFirstName = value
PropertyHasChanged("FirstName")
End If
End Set
End Property
Notice the calls to CanReadProperty and CanWriteProperty. The second parameter specifies that the method should throw an exception if the user is not authorized to perform the specific read/write operation.
The implementation of the CanReadProperty and CanWriteProperty are provided by the framework's base class but should be reproducible without adopting the entire CSLA framework. The implementations check an AuthorizationRules data structure which defines who is allowed/denied Read/Write access based on roles. Often, the AuthorizationRules structure is populated during object creation.
Exposing the same CanReadProperty and CanWriteProperty methods to your presentation-tier allows you to enable/disable UI elements based on the current user's access rights. For example:
FirstNameTextBox.ReadOnly = Not CanWriteProperty("FirstName", false)
Hopefully this information will provide you with a good starting point for developing your own implementation. If you're interested in learning more about CSLA then check out Expert C# 2008 Business Objects.
I suggest to use third party framework to achieve this. We use CSLA framework in our projects. It allow us to set authorization at field level.
I have a web application which has a Sql Server database on the backend. As the site will be rolled out to different clients (or different virtual directories in IIS), each client derivative of the site will use the same backend.
There is an admin page where text on the site can be changed (enter text in a listview, and choose the page to select where that text will show up, and also you can see company-specific details in the other listviews. As this is a shared database, that means a client can see each other's data.
What I am trying to do is store the accountId (a guid returned from the database from login_authenticate), and stick this into session. I then retrieve this on the admin page, and I want to use this value (But it's always 0000-0000 etc), to limit the records returned in the listview.
Is there an example of this? Also, how can I set the default value (this is in the where clause of SqlDataSource), to programatically what the account id is (so I can give me all records = what the current accountid is, or perhaps, what the login is - this is stored in the account table).
Thanks
This is what I tried.
What I am confused about, though, is whether the where clause, when using a session object, is getting an object that I have written the code to retrieve from the session, or an object I have only added but not retrieved. I get the accountID when logging in (verified via stepping in - obviously - or the login will fail).
I will try again with storing the object in session # the login page when I have just retrieved the accountid variable, and then retrieve it on another page.
For some reason I keep getting 0s so I will look at this in my application.
It sounds like your method should be working. I would follow a debugging process:
Check that you are getting the accountID value from the database. Print it on screen immediately after retrieving the value for the first time.
If this is working, store the value in the Session and immediately retrieve it, and check that you are getting the value back.
Create 2 test pages, one where you set the Session variable and another where you retrieve it.
I know this seems really basic, but the failure is being introduced somewhere in the above 3 places. If you can find which step fails, you will be able to fix it.