IdentityServer4 ConfigurationDbContext not loading client secrets - entity-framework

I want to display a list of clients and their secrets in an admin-only page of an MVC app baked into my IdentityServer project that is using EntityFramework.
I currently have a reference to ConfigurationDbContext in my controller from dependency injection and can access the client details. However, the ClientSecrets property of each client returned from the context does not have the secrets loaded, and is set to null. How can I force the context to load the ClientSecrets, which are stored in another table? Is there a different way I should be doing this?

Because the entity framework saved the client secrets in the separate table you have to use the entity framework's extension method - Include.
public class ApplicatonsService : IApplicationsService
{
private readonly ConfigurationDbContext is4Context;
public ApplicatonsService(ConfigurationDbContext is4Context)
{
this.is4Context = is4Context;
}
public Task<List<Client>> GetApplicationsAsync()
{
return Task.FromResult(is4Context.Clients
.Include(c => c.ClientSecrets)
.Select(c => c.ToModel()).ToList());
}
...
}

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

Read DbContextOptions value in Entity Framework core constructor

In my asp.net core service, I have this in the startup
services.AddDbContext<Mydb_Context>(
options => options.UseSqlServer(Configuration["Settings:ConnString"]));
and to make it works, I created also this snippet:
public Mydb_Context(DbContextOptions<Mydb_Context> options) : base(options)
{
//other stuff here
}
My problem now is: in "other stuff here" section, how can I read the options value and retrieve my connection string? Since there another dll and not the same project, I can't just read it again from the appsettings.json.
If I break my code during a debug session, I can see the connection string in the value inside this object, but I don't know how to get it properly. I see this value inside the "Extension" attribute of the options object.
So, how can I read the DbContextOptions value?
So, how can I read the DbContextOptions value?
You can (not publicly but using some EF Core internal infrastructure objects, in particular RelationalOptionsExtension class), but you don't need to.
To get the connection information, simply use context Database property and GetDbConnextion method:
public Mydb_Context(DbContextOptions<Mydb_Context> options) : base(options)
{
//other stuff here
var connectionString = this.Database.GetDbConnection().ConnectionString;
}

Web Api OData v4.0 and Memory Usage/Leak with Castle Windsor

I have a web project which is configured to provide an OData v 4.0 endpoint and is using the following components:
ASP.NET Web Api 5.2.2
Entity Framework 6.1.1
Owin 5.2.2
OData 5.3.1
The entire web application is using Windsor container from Castle Project.
The lifestyle of my controllers is per web request and same applies for all dependencies including DbContext.
The IoC is configured as following:
Owin Startup .cs
WindsorConfig.InitializeContainer(GlobalConfiguration.Configuration);
Configuration.cs
IWindsorContainer container = new WindsorContainer();
container.Install(FromAssembly.This());
container.Register(Component.For<IWindsorContainer>().Instance(container));
config.DependencyResolver = new WindsorDependencyResolver(container);
Installer.cs
container.Register(FromAssembly.This()
.BasedOn<ODataController>()
.LifestylePerWebRequest());
container.Register(
Component
.For<DbContext>()
.ImplementedBy<MyContext>()
.LifestylePerWebRequest());
So, an OData controller looks like this:
public class PersonsController : ODataController
{
#region Public Constructors
public PersonsController(ICommandDispatcher commandDispatcher, DbContext session)
{
this.commandDispatcher = commandDispatcher;
this.session = session;
}
public IQueryable<Person> Get()
{
return session
.Set<Person>()
.AsNoTracking();
}
public SingleResult<Person> Get([FromODataUri] Guid key)
{
return SingleResult.Create(session
.Set<Person>()
.AsNoTracking()
.Where(x => x.Id == key));
}
}
I am using Set.AsNoTracking() to avoid extra memory usage from Entity Framework.
Anyway, during time, after 100/150 queries the memory of the app pool keep raising of 1-2 MB and it never gets recycled.
Am I doing something wrong? Maybe Castle is not appropriate for this project and I have to use another IoC container?

How to specify EntityFramework ProviderName in an Azure Function

I'm trying to port some webjob code to the new Azure Functions. So far I've managed to import my DLL's and reference them succesfully, but when I use the connection string in my code, I get an error saying I have to add the ProviderName:
The connection string 'ConnectionString' in the application's
configuration file does not contain the required providerName
attribute."
Which is normally not a problem because in a webjob (or web app), this will be in the App or Web.config, and the connectionstring will simply be overwritten with whatever I entered in Azure.
With Azure Functions, I don't have a web.config (Although I tried adding one to no avail), so naturally the providername is missing.
How do I specify that?
EDIT:
After some playing around and some helpful tips by people below, I've almost managed to get it working.
What I do now is the following:
var connString = **MY CONN STRING FROM CONFIG**; // Constring without metadata etc.
EntityConnectionStringBuilder b = new EntityConnectionStringBuilder();
b.Metadata = "res://*/Entities.MyDB.csdl|res://*/Entities.MyDB.ssdl|res://*/Entities.MyDB.msl";
b.ProviderConnectionString = connString.ConnectionString;
b.Provider = "System.Data.SqlClient";
return new MyDB(b.ConnectionString);
Which gives me what I need for calling the database. I use a static method in a partial class to get an instance of the Database which runs the above code, and I decorate my MyDB Partial with [DbConfigurationType(typeof(MyDbConfiguration))]
I define that configuration as:
public class MyDBConfiguration: DbConfiguration
{
public MyDBConfiguration()
{
SetProviderFactory("System.Data.EntityClient", EntityProviderFactory.Instance);
}
}
My problem remains when I want to actually use the EF Entities. Here, it will try to initialize the database type using the original configuration, giving me the original error once again. As per this stack trace:
at Void Initialize()
at System.Data.Entity.Internal.EntitySetTypePair GetEntitySetAndBaseTypeForType(System.Type)
at Void InitializeContext()
at System.Data.Entity.Core.Objects.ObjectContext CreateObjectContextFromConnectionModel()
at Void Initialize()
at Boolean TryInitializeFromAppConfig(System.String, System.Data.Entity.Internal.AppConfig)
at Void InitializeFromConnectionStringSetting(System.Configuration.ConnectionStringSettings)
So how do I avoid this? I guess I need a way to hook into everything and run my custom setter..
In the end, Stephen Reindel pushed me in the right direction; Code-based Configuration for Entity Framework.
[DbConfigurationType(typeof(MyDBConfiguration))]
public partial class MyDB
{
public static MyDB GetDB()
{
var connString = **MY CONN STRING FROM SOMEWHERE**; // Constring without metadata etc.
EntityConnectionStringBuilder b = new EntityConnectionStringBuilder();
b.Metadata = "res://*/Entities.MyDB.csdl|res://*/Entities.MyDB.ssdl|res://*/Entities.MyDB.msl";
b.ProviderConnectionString = connString.ConnectionString;
b.Provider = "System.Data.SqlClient";
return new MyDB(b.ConnectionString);
}
public MyDB(string connectionString) : base(connectionString)
{
}
}
With MyDbConfiguration like this:
public class MyDBConfiguration: DbConfiguration
{
public MyDBConfiguration()
{
SetProviderServices("System.Data.SqlClient", SqlProviderServices.Instance);
SetDefaultConnectionFactory(new SqlConnectionFactory());
}
}
With the above code, EF never asks for AppConfig-related config files. But remember, if you have EF entries in your config file, it will attempt to use them, so make sure they're gone.
In terms of azure functions, this means I used the Azure Functions configuration panel in azure to punch in my ConnectionString without the Metadata and providername, and then loaded that in GetDB.
Edit: As per request, here is some explanatory text of the above:
You can't add EF metadata about the connection in Azure Functions, as they do not use an app.config in which to specify it. This is not a part of the connection string, but is metadata about the connection besides the connection string that EF uses to map to a specific C# Class and SQL Provider etc. To avoid this, you hardcode it using the above example. You do that by creating a class inheriting from DBConfiguration, and you mark (with an attribute on a partial class) your EF database class with that.
This DBConfiguration contains a different kind of way to instantiate a new database object, in which this metadata is hardcoded, but the connectionstring is retrieved from your app settings in Azure. In this example I just used a static method, but I guess it could be a new constructor also.
Once you have this static method in play, you can use that to get a new database in your database code, like this:
using (var db = MyDB.GetDB()) {
// db code here.
}
This allows you to use EntityFramework without an APP.Config, and you can still change the connectionstring using Azure Functions APP settings.
Hope that helps
Using this question you can set your default factory before opening the connection by having your personal DbConfiguration class (see this link also for usage):
public class MyDbConfiguration : DbConfiguration
{
public MyDbConfiguration()
{
SetDefaultConnectionFactory(new SqlConnectionFactory());
}
}
Now you need to tell your DbContext to use the new configuration. As using web.config or app.config is no option, you may use an attribute to add the configuration:
[DbConfigurationType(typeof(MyDbConfiguration))]
public class MyContextContext : DbContext
{
}
Now using a connection string on your DbContext will use the SQL provider by default.
Provided answer is perfect and it helped me a lot but it is not dynamic as I dont want to hardcode my connectionstring. if you are working the slots in azure functions. I was looking for a solution where I can use more than 1 connection strings. Here is my alternative approach step by step for anybody else struggling with this problem.
most important thing is that we understand local.settings.json file
IS NOT FOR AZURE. it is to run your app in the local as the name is
clearly saying. So solution is nothing to do with this file.
App.Config or Web.Config doesnt work for Azure function connection strings. If you have Database Layer Library you cant overwrite connection string using any of these as you would do in Asp.Net applications.
In order to work with, you need to define your connection string on the azure portal under the Application Settings in your Azure function. There is
Connection strings. there you should copy your connection string of your DBContext. if it is edmx, it will look like as below. There is Connection type, I use it SQlAzure but I tested with Custom(somebody claimed only works with custom) works with both.
metadata=res:///Models.myDB.csdl|res:///Models.myDB.ssdl|res://*/Models.myDB.msl;provider=System.Data.SqlClient;provider
connection string='data source=[yourdbURL];initial
catalog=myDB;persist security info=True;user
id=xxxx;password=xxx;MultipleActiveResultSets=True;App=EntityFramework
After you set this up, You need to read the url in your application and provide the DBContext. DbContext implements a constructor with connection string parameter. By default constructor is without any parameter but you can extend this. if you are using POCO class, you can amend DbContext class simply. If you use Database generated Edmx classes like me, you dont want to touch the auto generated edmx class instead of you want to create partial class in the same namespace and extend this class as below.
This is auto generated DbContext
namespace myApp.Data.Models
{
public partial class myDBEntities : DbContext
{
public myDBEntities()
: base("name=myDBEntities")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
}
this is the new partial class, you create
namespace myApp.Data.Models
{
[DbConfigurationType(typeof(myDBContextConfig))]
partial class myDBEntities
{
public myDBEntities(string connectionString) : base(connectionString)
{
}
}
public class myDBContextConfig : DbConfiguration
{
public myDBContextConfig()
{
SetProviderServices("System.Data.EntityClient",
SqlProviderServices.Instance);
SetDefaultConnectionFactory(new SqlConnectionFactory());
}
}
}
After all you can get the connection string from azure settings, in your Azure Function project with the code below and provide to your DbContext
myDBEntities is the name you gave in the azure portal for your connection string.
var connString = ConfigurationManager.ConnectionStrings["myDBEntities"].ConnectionString;
using (var dbContext = new myDBEntities(connString))
{
//TODO:
}
Adding an answer in the event you cannot simply change the way you instantiate you DbContext. This would occur if you are calling code that has DbContexts being instatiated with the parameter-less constructor.
It involves using a static constructor to read your connection string from the appsettings in the azure portal and passing it in to your DbContext base constructor. This allows you to circumvent the need for a providerName and also allows you to retain use of the portal configuration without needing to hardcode anything.
Please refer to my accepted answer here: Missing ProviderName when debugging AzureFunction as well as deploying azure function
Stumbled upon this and solved it like this, inside of the Azure Function.
public static class MyFunction
{
// Putting this in more than one place in your project will cause an exception,
// if doing it after the DbConfiguration has been loaded.
static MyFunction() =>
DbConfiguration.Loaded += (_, d) =>
d.AddDefaultResolver(new global::MySql.Data.Entity.MySqlDependencyResolver());
// The rest of your function...
//[FunctionName("MyFunction")]
//public static async Task Run() {}
}
You can access the site's App Settings by going to the portal, clicking Function app settings and then Configure app settings. That will open up a blade that allows you to set all the app settings for your function app. Just use the same key and value that you'd use for your web.config.

How to make Unity pass a constructor parameter based on Web API request?

I'm using Web API in a multi-tenant environnement. Each tenant has their own database with an identical structure. The tenant name is included in the URL as a sub-domain : http://{tenant}.mysite.com/api/doodad/action
I've written a message filter (DelegatingHandler) to pull out the tenant name and find the corresponding database. I'm currently stashing this value in the request parameters.
Pretty much every controller action needs access a DbContext pointing to this database. I'm not using a repository pattern. I've created a second constructor on my DbContext that accepts the database name.
I'm using Unity to inject an instance of the DbContext in the ApiController. How can I get Unity to call the second constructor on the DbContext passing in the value from the request parameter?
I haven't tried this yet, but I think it should work...
You could create an action filter that would add the HttpRequestMessage to the current hierarchical container and then register your custom DbContext type with an InjectionFactory to pull out the HttpRequestMessage and get the DB name.
Here's some untested code...
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = false)]
public class RegisterHttpRequestActionAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
var container = GlobalConfiguration.Configuration.DependencyResolver.GetService(typeof(IUnityContainer)) as IUnityContainer;
container.RegisterInstance(actionContext.Request, new HierarchicalLifetimeManager());
}
}
and the configuration...
IUnityContainer rootContainer = new UnityContainer();
GlobalConfiguration.Configuration.DependencyResolver = new UnityHierarchicalDependencyResolver(rootContainer);
GlobalConfiguration.Configuration.Filters.Add(new RegisterHttpRequestActionAttribute());
rootContainer.RegisterType<MyDbContext>(new HierarchicalLifetimeManager(), new InjectionFactory(container =>
new MyDbContext(container.Resolve<HttpRequestMessage>().Properties["TenantDB"] as string)));
(FYI, I reference the Unity.AspNet.WebApi NuGet package to get the UnityHierarchicalDependencyResolver)