EF + UnitOfWork + SharePoint RunWithElevatedPrivileges - entity-framework

In our SharePoint application we have used the UnitOfWork + Repository patterns together with Entity Framework. To avoid the usage of the passthrough authentication we have developed a piece of code that impersonate a single user before creating the ObjectContext instance in a similar way that is described in "Impersonating user with Entity Framework" on this site.
The only difference between our code and the referred question is that, to do the impersonation, we are using RunWithElevatedPrivileges to impersonate the Application Pool identity as in the following sample.
SPSecurity.RunWithElevatedPrivileges(delegate() {
using (SPSite site = new SPSite(url)) {
_context = new MyDataContext(ConfigSingleton.GetInstance().ConnectionString);
}
});
We have done this way because we expected that creating the ObjectContext after impersonation and, due to the fact that Repositories are receiving the impersonated ObjectContext would solve our requirement.
Unfortunately it's not so easy. In fact we experienced that, even if the ObjectContext is created before and under impersonation circumstances, the real connection is made just before executing the query, and so does not use impersonation, which break our requirement.
I have checked the ObjectContext class to see if there was any event through which we can inject the impersonation but unfortunately found nothing.
Any help?

We had a simillar problem when we used LinqToSharePoint. The DataContext is created from the HttpContext.Current and did not consider the RunWithElevatedPrivileges method. We did a nasty workaround that we backed up the original HttpContext, created a new dummy HttpContext in the RunWithElevatedPrivileges method and the problem went away. Obviously we set the context to the original afterwards.
Edit:
You can use the method below to create new dummy HttpContext.Call this method as first in your RunWithElevatedPrivileges. In the normal context just backup your currenct context with var backupContext = HttpContext.Current and after everything is done just set the context back.
private void SetNewContextWeb(SPWeb oWeb)
{
HttpRequest httpRequest = new HttpRequest(string.Empty, oWeb.Url, string.Empty);
HttpContext.Current = new HttpContext(httpRequest, new HttpResponse(new System.IO.StringWriter()));
SPControl.SetContextWeb(HttpContext.Current, oWeb);
}

Related

ASP.NET Core 6 web app with Identity - Ind. Accounts. How can I add a role and add 2 users to that role?

Ok, I remember back in regular ASP.NET 4 (before .NET Core - 2015 ish) it was not this convoluted to add a user to a role. But now I found it to be very difficult.
Using Sqlite database, and all the scaffolding and account creation works great. Even imported my contact subscriber list and it seamlessly created the CRUD - awesome.
Now I only need to restrict this page to Admins only, which I did this and works -- no access. I am on the step to add an Admin role and add a user to it.
After reading and trying code from many sites I find myself here for some direction.
Also I see a role and role claim which is confusing..
Ref: How to create roles in ASP.NET Core and assign them to users?
This may work if I knew where to put this for it to find the proper references.
private readonly UserManager<ApplicationUser> _userManager;
private readonly RoleManager<ApplicationRole> _rolesManager;
Not sure where the ApplicationUser and ApplicationRole is coming from.
Assuming you have correctly configured Identity in your application with something similar to the following
builder.Services
.AddIdentity<IdentityUser, IdentityRole>(options =>
options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<WebApplication2Context>();
(in your case, IdentityUser is probably going to map to your AspNetUser class and IdentityRole would be your AspNetRole class, though I'm not sure where you got those classes or whether they properly inherit IdentityUser<T> and IdentityRole<T> - if they don't, you've gone down a dark and scary road...)
Once that's done (and you've applied all the EF Core migrations, etc. to get your database correctly built), you can do something like this to add a role and add a user to it
#page
#model IndexModel
#using Microsoft.AspNetCore.Identity
#inject UserManager<IdentityUser> _userManager
#inject RoleManager<IdentityRole> _roleManager
#{
ViewData["Title"] = "Home page";
await _roleManager.CreateAsync(new IdentityRole("Admin"));
await _userManager.CreateAsync(new IdentityUser("foobar") { Email = "foo#bar.com" });
var newUser = await _userManager.FindByNameAsync("foobar");
await _userManager.AddToRoleAsync(newUser, "Admin");
}
If you're not sure your existing AspNetXxx classes are correct or correctly map to the related Identity classes, I'd suggest you start over and use the default implementations as much as possible. You can read about Identity Model Customization in ASP.NET Core to learn about the model and how all the different tables work together.
I think you need to "link" your "MyPolicyName" to your MVC.
https://learn.microsoft.com/en-us/aspnet/core/security/authorization/policies?view=aspnetcore-6.0#apply-policies-to-razor-pages
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace AuthorizationPoliciesSample.Pages;
[Authorize(Policy = "AtLeast21")]
public class AtLeast21Model : PageModel { }
...
Because of this "string-matching-magic" I would create a single-source-of-truth class.
public static class MyPolicyNames
{
public static string AtLeast21PolcyName = "AtLeast21PolicyName";
}
and refer to this const in both places. And eliminate future "where are the magic-strings" scavenger hunts.
=============
So you are using a Policy, but your "policy rule" is to then check a Role.
Should you be just using Role?
https://learn.microsoft.com/en-us/aspnet/core/security/authorization/roles?view=aspnetcore-6.0
I guess M$ says its "ok"
https://learn.microsoft.com/en-us/aspnet/core/security/authorization/roles?view=aspnetcore-6.0#policy-based-role-checks

How to get user's id inside an Entity Framework DbContext

I need to track database changes in net core 2.0 EF. It all works but I don't know how to get userId inside DbContext. I need to get UserId to assign changes to the user. I cannot simply run _userManager.GetUserId(HttpContext.User).ToString();. Any suggestions? Thanks in advance for your help.
In your ConfigureServices method in Startup.cs add:
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddEntityFrameworkSqlServer();
The services.AddEntityFrameworkSqlServer() bit adds the service resolver into your context, allowing you to utilize theGetService extension method on your context.
Then, inside your context you can do:
var httpContextAccessor = this.GetService<IHttpContextAccessor>();
The result may be null, particularly if the context is not running within a web application (i.e. during migrations, console apps utilizing your context, etc.). As a result, you should do proper null checking. The following uses C# 6's null-conditional operator:
var userId = httpContextAccessor?.HttpContext.User.FindFirstValue(ClaimTypes.NameIdentifier);
Then, userId will either have the id of the currently logged in user or be null.

Performing Explicit Route Mapping based upon Web Api v2 Attributes

I'm upgrading a custom solution where I can dynamically register and unregister Web Api controllers to use the new attribute routing mechanism. However, it seems to recent update to RTM break my solution.
My solution exposes a couple of Web Api controllers for administration purposes. These are registered using the new HttpConfigurationExtensions.MapHttpAttributeRoutes method call.
The solution also allows Web Api controllers to be hosted in third-party assemblies and registered dynamically. At this stage, calling HttpConfigurationExtensions.MapHttAttributeRoutes a second time once the third-party controller is loaded would raise an exception. Therefore, my solution uses reflection to inspect the RoutePrefix and Route attributes and register corresponding routes on the HttpConfiguration object.
Unfortunately, calling the Web Api results in the following error:
"No HTTP resource was found that matches the request URI".
Here is a simple controller that I want to use:
[RoutePrefix("api/ze")]
public sealed class ZeController : ApiController
{
[HttpGet]
[Route("one")]
public string GetOne()
{
return "One";
}
[HttpGet]
[Route("two")]
public string GetTwo()
{
return "Two";
}
[HttpPost]
[Route("one")]
public string SetOne(string value)
{
return String.Empty;
}
}
Here is the first solution I tried:
configuration.Routes.MapHttpRoute("ZeApi", "api/ze/{action}");
Here is the second solution I tried:
var type = typeof(ZeController);
var routeMembers = type.GetMethods().Where(m => m.IsPublic);
foreach (MethodInfo method in routeMembers)
{
var routeAttribute = method.GetCustomAttributes(false).OfType<RouteAttribute>().FirstOrDefault();
if (routeAttribute != null)
{
string controllerName = type.Name.Substring(0, type.Name.LastIndexOf("Controller"));
string routeTemplate = string.Join("/", "api/Ze", routeAttribute.Template);
configuration.Routes.MapHttpRoute(method.Name, routeTemplate);
}
}
I also have tried a third solution, whereby I create custom classes that implement IHttpRoute and trying to register them with the configuration to no avail.
Is it possible to use legacy-style route mapping based upon the information contained in the new routing attributes ?
Update
I have installed my controller in a Web Application in order to troubleshoot the routing selection process with the Web Api Route Debugger. Here is the result of the screenshot:
As you can see, the correct action seems to be selected, but I still get a 404 error.
Update2
After further analysis, and per Kiran Challa's comment below, it seems that the design of Web Api prevents mixing attribute routing and conventional routing, and that what I want to do is not possible using this approach.
I have created a custom attribute [RouteEx] that serves the same purpose of the Web Api [Route] attribute, and now my code works perfectly.
I guess, since this is not possible using the conventional attribute routing, none of the answers on this question could legitimately be consisered valid. So I'm not nominating an answer just yet.
You shouldn't be required to use reflection and inspect the attribute-routing based attributes yourself. Attribute routing uses existing Web API features to get list of controllers to scan through.
Question: Before the switch to attribute routing, how were you loading these assemblies having the
controllers?
If you were doing this by IAssembliesResolver service, then this solution should work even with attribute routing and you should not be needing to do anything extra.
Regarding your Update: are you calling MapHttpAttributeRoutes?

structuremap entity framework 4 connection

I am using the following Structuremap bootstrapping code for my entity framework 4 entities:
x.For<XEntities>().LifecycleIs(Lifecycles.GetLifecycle(InstanceScope.PerRequest)).Use(() => new XEntities());
But when I do two nearly simultaneous requests, I get the following exception:
EntityException:The underlying provider failed on Open.
{"The connection was not closed. The connection's current state is connecting."}
I am using ASP.NET MVC 2, en have the following in my Application_Start()
EndRequest += new EventHandler(MvcApplication_EndRequest);
void MvcApplication_EndRequest(object sender, EventArgs e)
{
ObjectFactory.ReleaseAndDisposeAllHttpScopedObjects();
}
What can I do to fix this?
[edit]
this happens on a page with several images on it. The images come from the database, served by an Controller Action, which reads the image from the database, and sends it as a file result to the browser. I think that asp.net is breaking down my objectcontext, and closing my db connection when the requests for the images come in, and the exception is thrown.
What I need now, is a correct way to manage the lifetime of the object context in the good way.
Why are you assigning a delegate for EndRequest in Application_Start()?
Just hook directly into the event:
protected void Application_EndRequest()
{
ObjectFactory.ReleaseAndDisposeAllHttpScopedObjects();
}
Also, i have never used that syntax before, this is how i do it:
For<XEntities>().HybridHttpOrThreadLocalScoped().Use<XEntities>()
Also, at what point do you new up your Data Context? Can you show some code?

Using Ninject With Entity Framework

I have a repository Class which takes in a ObjectContext called "TestDB". I when I launch my web application i'm getting a "Unable to load the specified metadate resource", almost like its not picking up the connection settings from my web.config file anymore.
Here is a snippet of my code.
[Inject]
public SqlCatelogRepository(){
_dataContext = new SQLDb();
//EF Entity, should pickup connection settings from web.config
}
Once get what is going on there I would like to pass in my DataContenxt but I can't seem to wrap my head around how this should look in the Ninject Mapping.
Try something like:
[Inject]
public SqlCatelogRepository(){
_dataContext = kernel.Get<SQLDb>();
//EF Entity, should pickup connection settings from web.config
}
This will give NInject a chance to intercept your activation. Then your mappings can apply.
You might want to check out the "Service Locator" approach that Nate wrote about: http://kohari.org/2008/06/18/playing-nice-with-service-locators