{"id":2283,"date":"2007-12-10T22:25:54","date_gmt":"2007-12-10T22:25:54","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/odatateam\/2007\/12\/10\/ado-net-data-services-dec2007-ctp-validation-and-access-control\/"},"modified":"2007-12-10T22:25:54","modified_gmt":"2007-12-10T22:25:54","slug":"ado-net-data-services-dec2007-ctp-validation-and-access-control","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/odata\/ado-net-data-services-dec2007-ctp-validation-and-access-control\/","title":{"rendered":"ADO.NET Data Services Dec2007 CTP  &#8211; Validation and Access Control"},"content":{"rendered":"<p><i>Note: This post refers to the CTP available <a href=\"https:\/\/www.microsoft.com\/downloads\/details.aspx?FamilyId=A9C6BC06-B894-4B11-8300-35BD2F8FC908&amp;displaylang=en\">here<\/a>. <\/i>  <\/p>\n<p>When building frameworks to expose and consume data on the web, access policy and data validation are always one of the first topics discussed during the design process, at conferences, etc.&nbsp; Given this, we thought the topic would be a good one to kick off a series of blog posts about the feature set of the recently announced Dec 07 CTP of the product.&nbsp; If you haven\u2019t seen ADO.NET Data Services \/ \u201cProject Astoria\u201d before, I suggest checking out <a href=\"http:\/\/astoria.mslivelabs.com\">http:\/\/astoria.mslivelabs.com<\/a> for an overview and \u201cgetting started\u201d style walkthroughs of the product.&nbsp; <\/p>\n<p><b><\/b> <\/p>\n<p><b>Authentication<\/b>  <\/p>\n<p>Before getting into access control and validation, I need to put in a quick note about authentication to set the stage.&nbsp; Data services do not directly implement an authentication scheme. Instead, they rely on the authentication infrastructure of the technology (ex. WCF+ASP.NET, WCF, etc) hosting the data service. If you have an ASP.NET site that uses authentication (either one of the built-in authentication schemes or a custom one that sets the HTTP context principal appropriately), Astoria will simply leverage that mechanism to establish the current identity (principal) for a given request.&nbsp; Ok, now that we know how data services determines <i>who<\/i> is making a request, its\u2019 time to look at how to implement access control and perform validation based on this information.  <\/p>\n<p><b><\/b> <\/p>\n<p><b>Service-wide Access Control<\/b>  <\/p>\n<p>By default all entities, Service Operations and metadata describing any resources in a data service cannot be retrieved.&nbsp; Said another way, by default a data service is completely locked down with no read or write access.&nbsp; This is one of the significant differences from previous (prototype) releases of Astoria.&nbsp; In prior releases, a data service was fully open by default to enable easily creating and evaluating our ideas for what data services on the web might look like.&nbsp; Now that the project has moved out from incubation and into an official product release, we changed such that services are locked down out of the box to align with typical security requirements of production web infrastructures.  <\/p>\n<p>One of the first steps a data service developer needs to take is to open up access to the appropriate resources in the data service.&nbsp; One way to do this is to set service wide, access control policy by adding a data service initialization method.&nbsp; The code below shows what a typical data service created over the northwind sample database would look like if only the Customers, Orders and Products sets were exposed.&nbsp; <\/p>\n<p><font face=\"Courier New\" size=\"2\">public class MyDataService : WebDataService&lt;Northwind&gt;<\/font> <\/p>\n<p><font face=\"Courier New\" size=\"2\">{<\/font>  <\/p>\n<blockquote>\n<p><font face=\"Courier New\" size=\"2\"> public static void InitializeService&nbsp;&nbsp; <\/font> <\/p>\n<p><font face=\"Courier New\" size=\"2\">&nbsp;&nbsp; (<\/font><font face=\"Courier New\" size=\"2\">IWebDataServiceConfiguration configuration)<\/font>  <\/p>\n<p><font face=\"Courier New\" size=\"2\"> {<\/font> <\/p>\n<\/blockquote>\n<blockquote>\n<p><font face=\"Courier New\" size=\"2\">&nbsp;&nbsp; \/\/ URI \u2018\/Customers\u2019 entities is enabled for all&nbsp;&nbsp;&nbsp; <\/font><\/p>\n<\/blockquote>\n<blockquote>\n<p><font face=\"Courier New\" size=\"2\">&nbsp;&nbsp; \/\/read and write <\/font><font face=\"Courier New\" size=\"2\">operations<\/font> <\/p>\n<\/blockquote>\n<p><font face=\"Courier New\" size=\"2\">&nbsp;&nbsp;&nbsp; configuration.SetResourceContainerAccessRule<\/font> <\/p>\n<blockquote>\n<p><font face=\"Courier New\" size=\"2\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (&#8220;Customers&#8221;, <\/font><font face=\"Courier New\" size=\"2\">ResourceContainerRights.All);<\/font> <\/p>\n<\/blockquote>\n<p><font face=\"Courier New\" size=\"2\">&nbsp;&nbsp;&nbsp; <\/font> <\/p>\n<p><font face=\"Courier New\" size=\"2\">&nbsp;&nbsp;&nbsp;&nbsp; \/\/&nbsp; URI \u2018\/Orders\u2019 is disabled, but \u2018\/Orders(1)\u2019 <\/font> <\/p>\n<p><font face=\"Courier New\" size=\"2\">&nbsp;&nbsp;&nbsp;&nbsp; \/\/ is enabled for read <\/font><font face=\"Courier New\" size=\"2\">only <\/font> <\/p>\n<p><font face=\"Courier New\" size=\"2\">&nbsp;&nbsp;&nbsp;&nbsp; configuration.SetResourceContainerAccessRule<\/font> <\/p>\n<blockquote>\n<p><font face=\"Courier New\" size=\"2\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (&#8220;Orders&#8221;, R<\/font><font face=\"Courier New\" size=\"2\">esourceContainerRights.ReadSingle);<\/font> <\/p>\n<\/blockquote>\n<p><font face=\"Courier New\" size=\"2\"><\/font>&nbsp; <\/p>\n<blockquote>\n<p><font face=\"Courier New\" size=\"2\">&nbsp;&nbsp;&nbsp; \/\/&nbsp; Can insert and update, but not delete <\/font> <\/p>\n<p><font face=\"Courier New\" size=\"2\">&nbsp;&nbsp;&nbsp; \/\/ Products<\/font> <\/p>\n<\/blockquote>\n<p><font face=\"Courier New\" size=\"2\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; configuration.SetResourceContainerAccessRule<\/font> <\/p>\n<p><font face=\"Courier New\" size=\"2\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (&#8220;Products&#8221;, <\/font><font face=\"Courier New\" size=\"2\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/font> <\/p>\n<blockquote>\n<p><font face=\"Courier New\" size=\"2\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ResourceContainerRights.WriteInsert |<\/font> <\/p>\n<\/blockquote>\n<p><font face=\"Courier New\" size=\"2\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ResourceContainerRights.WriteUpdate);<\/font>  <\/p>\n<p><font face=\"Courier New\" size=\"2\">&nbsp;&nbsp; }<\/font>  <\/p>\n<p><font face=\"Courier New\" size=\"2\">}<\/font>  <\/p>\n<p>The access policies in the snippet above are applied to all requests to the data service and are related to entity sets, not particular URIs.&nbsp; For example, the policy described by the call: \u2018SetResourceContainerAccessRule(&#8220;Customers&#8221;,ResourceContainerRights.All)\u2019 would apply to a request sent to&nbsp; \u2018\/Customers(\u2018ALFKI\u2019)\u2019 as well as&nbsp; \u2019\/Orders(1)\/Customers\u2019.&nbsp; Basically, the rule is no matter how you address an entity or entity set, the rules in the init method apply equally.&nbsp; All other entities not explicitly enabled in the initialize service method will not be exposed by the data service.&nbsp; For example, the northwind db also includes Employee entities.&nbsp; Since the initialization method does not say anything about Employees, any request to retrieve an employee entity will result in an HTTP 404 (Not Found) response.&nbsp; In addition accessing the endpoint to retrieve the service\u2019s metadata (ex. <a href=\"http:\/\/host\/service.svc\/$metadata\">http:\/\/host\/service.svc\/$metadata<\/a>) will return a document which only describes those resources where were explicitly made visible.  <\/p>\n<p><b>Per Request Access Control &amp; Validation<\/b>  <\/p>\n<p>Many data services will need to need to run validation logic when entities enter the data service (for inserts, updates or deletes) and\/or restrict access to entities on a per request basis.&nbsp; For these scenarios <i>Interceptors<\/i> are used which enable a data service developer to plug in custom validation or access policy logic into the request\/response processing pipeline of a data service.&nbsp; We decided to take the approach of providing infrastructure to build out custom per request access policy instead of defining an access policy model specific to ADO.NET Data Services because we recognize that each application, business, etc have different requirements in this space and that we could add value by providing comprehensive infrastructure elements for one to build custom access policy.&nbsp; <\/p>\n<p>For example, assume you want to implement a policy which enabled customers to only retrieve their orders and not orders placed by other customers.&nbsp;&nbsp; The example below shows a <i>query interceptor<\/i> that implements this access policy.&nbsp; The important aspect to note about query interceptors is that they accept as a parameter an instance of IQueryable&lt;T&gt; which represents the query the system will push down to the underlying data store to retrieve the requested entity.&nbsp; As shown in the example, this approach enables access policy to be defined using query composition eliminating any added round trips to the data store to retrieve access control information.&nbsp; The example assumes the data service is hosted within a WCF+ASP.NET web application with authentication enabled as the ASP.Net HTTPContext object is used to retrieve the principle of the current request.&nbsp; <\/p>\n<p>using System;  <\/p>\n<p>using System.Web;  <\/p>\n<p>using System.Collections.Generic;  <\/p>\n<p>using System.ServiceModel.Web;  <\/p>\n<p>using System.Linq;  <\/p>\n<p>using Microsoft.Data.Web;  <\/p>\n<p>using NorthwindModel;  <\/p>\n<p>namespace TestApplication  <\/p>\n<p>{  <\/p>\n<p> public class nw :&nbsp;&nbsp; WebDataService&lt;NorthwindModel.NorthwindEntities&gt;  <\/p>\n<p> {  <\/p>\n<blockquote>\n<p>public static void InitializeService( <\/p>\n<\/blockquote>\n<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; IWebDataServiceConfiguration configuration)  <\/p>\n<p>&nbsp;&nbsp; {  <\/p>\n<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; configuration.SetResourceContainerAccessRule(&#8220;Customers&#8221;,  <\/p>\n<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ResourceContainerRights.All);  <\/p>\n<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; configuration.SetResourceContainerAccessRule(&#8220;Orders&#8221;,  <\/p>\n<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ResourceContainerRights.ReadSingle);  <\/p>\n<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; configuration.SetResourceContainerAccessRule(&#8220;Products&#8221;,  <\/p>\n<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ResourceContainerRights.WriteInsert |  <\/p>\n<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ResourceContainerRights.WriteUpdate);  <\/p>\n<p>&nbsp;&nbsp;&nbsp; }  <\/p>\n<p>&nbsp;&nbsp;&nbsp; <\/p>\n<blockquote>\n<p>[QueryInterceptor(&#8220;Orders&#8221;)] <\/p>\n<\/blockquote>\n<p>&nbsp;&nbsp;&nbsp; public IQueryable&lt;Orders&gt; OnQueryOrders(IQueryable&lt;Orders&gt;  <\/p>\n<blockquote>\n<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; orderQuery) <\/p>\n<\/blockquote>\n<p>&nbsp;&nbsp;&nbsp; {  <\/p>\n<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return from o in orderQuery  <\/p>\n<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; where o.Customers.ContactName ==  <\/p>\n<blockquote>\n<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; HttpContext.Current.User.Identity.Name <\/p>\n<\/blockquote>\n<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; select o;  <\/p>\n<p>&nbsp;&nbsp;&nbsp; } <\/p>\n<p>&nbsp; <\/p>\n<p> }  <\/p>\n<p>}  <\/p>\n<p>To hook into update operations (insert, update &amp; delete), <i>change interceptors<\/i> are used.&nbsp; The example below validates all the Product entities that are created are not discontinued.&nbsp;&nbsp; This would also be the place to implement per request access policy to restrict inserts, updates and\/or deletes per request.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/p>\n<p>[ChangeInterceptor(&#8220;Products&#8221;)]  <\/p>\n<p>public void OnChangeCategories(Products p, ResourceActions action){  <\/p>\n<blockquote>\n<p>if(action == ReceiveEntityOperation.Insert) { <\/p>\n<\/blockquote>\n<blockquote>\n<p>&nbsp; if(p.Discontinued) { <\/p>\n<\/blockquote>\n<blockquote>\n<p>&nbsp;&nbsp;&nbsp; throw new WebDataServiceException(400, <\/p>\n<\/blockquote>\n<blockquote>\n<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#8220;Cannot add discontinued products&#8221;); <\/p>\n<\/blockquote>\n<p>&nbsp;&nbsp;&nbsp;&nbsp; }  <\/p>\n<p> }  <\/p>\n<p>}  <\/p>\n<p>To summarize, services created using the ADO.NET Data Services framework are locked down by default and resources must be explicitly exposed.&nbsp; Once exposed, the service developer has a number of hooks at their disposal to implement per request access control and business rule validation.&nbsp;&nbsp; <\/p>\n<p>What do you think of this approach?&nbsp; Is there something we should change\/alter\/add\/etc to better meet your requirements?&nbsp;&nbsp; We look forward to hearing your feedback and continuing our <a href=\"https:\/\/blogs.msdn.com\/astoriateam\/archive\/2007\/07\/20\/transparency-in-the-design-process.aspx\">open design process<\/a> on this blog.  <\/p>\n<p>&nbsp; <\/p>\n<p>Mike Flasko  <\/p>\n<p>Program Manager  <\/p>\n<p>&nbsp;<\/p>\n<p>This post is part of the transparent design exercise in the Astoria Team. To understand how it works and how your feedback will be used please look at <a href=\"https:\/\/blogs.msdn.com\/astoriateam\/archive\/2007\/07\/20\/transparency-in-the-design-process.aspx\">this post<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Note: This post refers to the CTP available here. When building frameworks to expose and consume data on the web, access policy and data validation are always one of the first topics discussed during the design process, at conferences, etc.&nbsp; Given this, we thought the topic would be a good one to kick off a [&hellip;]<\/p>\n","protected":false},"author":512,"featured_media":3253,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[27],"class_list":["post-2283","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-odata","tag-design-notes"],"acf":[],"blog_post_summary":"<p>Note: This post refers to the CTP available here. When building frameworks to expose and consume data on the web, access policy and data validation are always one of the first topics discussed during the design process, at conferences, etc.&nbsp; Given this, we thought the topic would be a good one to kick off a [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/odata\/wp-json\/wp\/v2\/posts\/2283","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/odata\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/odata\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/odata\/wp-json\/wp\/v2\/users\/512"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/odata\/wp-json\/wp\/v2\/comments?post=2283"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/odata\/wp-json\/wp\/v2\/posts\/2283\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/odata\/wp-json\/wp\/v2\/media\/3253"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/odata\/wp-json\/wp\/v2\/media?parent=2283"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/odata\/wp-json\/wp\/v2\/categories?post=2283"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/odata\/wp-json\/wp\/v2\/tags?post=2283"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}