I am in the process of looking at an API and I see the following two calls:
API.Users.Roles.getAllRoles();
API.Admin.Roles.getAllRoles();
What I would like to know is how each of these call is used within
the context of a Web program. Since both Admin and Users are properties,
what exactly is the get; set; doing? How does the call know which Admin
(or user) is making the call?
My hunch is that this has something to do with how the API class is
instantiated (and session?) but I'd appreciate a walk-through on what is
going on here so I fully understand it.
The (abbreviated) class structure looks like the following:
public class API()
{
public Admin Admin { get; private set; }
public Users Users { get; private set; }
}
public class Users
{
public Roles Roles { get; private set; }
...
}
public class Roles
{
public override string[] GetAllRoles()
{
...
}
}
Thanks in advance.
It will check the current user name from the current principal (HttpContext.Current.User.Identity.Name) which uses forms/windows account depending on setup, or if not in the web environment, it will use the current windows user logged into the system.
To me it seems that they have a custom role provider and are therefore overriding the GetAllRoles method so that the roles can be obtained from the datasource. Without seeing further details, I can only assume, but when a user registers, they're probably assigned a particular role. They can then use the Roles.IsUserInRole method to detect what role the user is assigned to. There's more on custom role providers here which will explain why methods are being overwritten.
Related
I have the (almost) worst of multi tenancy. I'm building a asp.net core website that I'm porting a bunch of pokey little intranet sites to. Each subsite will be an asp.net Area. I have an IdentityContext for the Identity stuff. I have multiple copies of vendor databases, each of those with multiple tenants. The ApplicationUserclass has an OrgCode property that I want to use to switch the db context.
I can see myself needing something that maps User.OrgCode and Area to a Connection string
There are many partial examples of this on Stack Overflow. I am very confused after an afternoons reading. The core of it seams to be:
remove DI dbcontext ref from the constructor args.
Instantiate the dbcontext in the controller constructor.
Use dbcontext as before.
Am I on the right track?
Any coherent examples?
Edit 2020/07/09
This has unfortunately become more pressing.
The Identity database is tenant agnostic. Every user in Identity has an OrgCode identifier. (Custom user property).
Each server has multi tenancy built in through the use of 'cost centers'. The server has a collection of databases named the same on every server.
core vendor database
custom database where we store our extensions
logs database for our job output
There are also small application specific databases that already use an Org Code to identify a user
Server A - 1 Org Code
Server B - 4 Org Codes
Server C - 3 Org Codes engaged in project, 50+ not yet (mostly small)
Server D - No Org Codes engaged as of now. 80+ on server. (soon)
It is not possible to consolidate all the organisations onto one server. There are legal and technical ramifications. Each server has hundreds of remote transponders reporting to them that would need updating. The data these supply is what our custom jobs work with.
The dream is to continue to use DI in each page, passing in the contexts as required. The context would then be smart enough to pick the correct underlying connection details based on the OrgCode of the username.
I hesitate to use the word proxy because it seems heavily loaded in this space.
Hell, even using a switch statement would be fine if I knew where to put it
Desired effect User from Org XYZ loads page that requires Vendor database, they get the one from the server that XYZ maps to.
Edit 2020/07/13
To tidy up referenceing, I've switched the OrgCode and Server to Enums. The context inheritance is as follows
DbContext
CustLogsContext
public virtual ServerEnum Server
{
get
{
return ServerEnum.None;
}
}
DbSet (etc)
CustLogsServerAContext
public override ServerEnum Server
{
get
{
return ServerEnum.ServerA;
}
}
CustLogsServerBContext (etc)
CustLogsServerCContext (etc)
CustLogsServerDContext (etc)
VendorContext
VendorServerAContext
VendorServerBContext (etc)
VendorServerCContext (etc)
VendorServerDContext (etc)
I've also created a static class OrgToServerMapping that contains a dictionary mapping OrgCodes to Servers. Currently hardcoded, will change eventually to load from config, and add a reload method.
Currently thinking I need a class that collects the contexts Would have a Dictionary<serverEnum, dbcontext> and be registered as a service. Pretty sure I'd need a version of the object for each inherited dbcontext, unless someone knows ome polymorphic trick I can use
I work on a similar system with thousands of databases, but with LinqToSql instead of EF (I know...). Hopefully the general ideas translate. There are connection pool fragmentation issues that you have to contend with if you end up with many databases, but for just your four databases you won't have to worry about that.
I like these two approaches - they both assume that you can set up the current ApplicationUser to be injected via DI.
Approach #1: In Startup, configure the DI that returns the data context to get the current user, then use that user to build the correct data context. Something like this:
// In Startup.ConfigureServices
services.AddScoped<ApplicationUser>((serviceProvider) =>
{
// something to return the active user however you're normally doing it.
});
services.AddTransient<CustLogsContext>((serviceProvider) =>
{
ApplicationUser currentUser = serviceProvider.GetRequiredService<ApplicationUser>();
// Use your OrgToServerMapping to create a data context
// with the correct connection
return CreateDataContextFromOrganization(currentUser.OrgCode);
});
Approach #2: Rather than injecting the CustLogsContext directly, inject a service that depends on the active user that is responsible for building the data context:
// In Startup.ConfigureServices
services.AddScoped<ApplicationUser>((serviceProvider) =>
{
// something to return the active user however you're normally doing it.
});
services.AddTransient<CustLogsContextWrapper>();
// In its own file somewhere
public class CustLogsContextWrapper
{
private ApplicationUser currentUser;
public CustLogsContextWrapper(ApplicationUser currentUser)
{
this.currentUser = currentUser;
}
public CustLogsContext GetContext()
{
// use your OrgToServerMapping to create a data context with the correct connection;
return CreateDataContextFromOrganization(user.OrgCode);
}
}
Personally I prefer the latter approach, because it avoids a call to a service locator in Startup, and I like encapsulating away the details of how the data context is created. But if I already had a bunch of code that gets the data context directly with DI, the first one would be fine.
I have created a multitenancy implementation as follows (which could scale endlessly in theorie). Create a multitenancy database (say tenantdb). Easy. But the trick is to store connectionstring details for each tenant (your target databases). Along side your user orgCode etc.
I can see myself needing something that maps User.OrgCode and Area to a Connection string
So the way to map it in code is to feed your dbcontext whith your target tenant connectionstring, which you get from your tenantdb. So you would need anohter dbcontext for you tenantdb. So first call your tenantdb get the correct tenant connectionstring by filtering with your user orgcode. And then use it to create a new target dbcontext.
The dream is to continue to use DI in each page, passing in the contexts as required. The context would then be smart enough to pick the correct underlying connection details based on the OrgCode of the username.
I have this working with DI.
I created UI elements for crud operations for this tenantdb, so I can update delete add connection string details and other needed data. The Password is encrypted on save and decrypted on the get just before passing to your target dbcontext.
So I have two connection strings in my config file. One for the tenantdb and one for a default target db. Which can be an empty/dummy one, as you probably encounter application startup errors thrown by your DI code if you don't have one, as it will most likely auto search for a connectionstring.
I also have switch code. This is where a user can switch to anohter tenant. So here the user can choose from all the tenants it has rights to (yes rights are stored in tenantdb). And this would again trigger the code steps described above.
Cheers.
Took this Razor Pages tutorial as my starting point.
This way you can have very lousily coupled target databases. The only overlap could be the User ID. (or even some token from Azure,Google,AWS etc)
Startup.
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
services.AddDbContext<TenantContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("TenantContext")));
//your dummy (empty) target context.
services.AddDbContext<TargetContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("TargetContext")));
}
IndexModel (Tenant pages).
public class IndexModel : PageModel
{
private readonly ContosoUniversity.Data.TenantContext _context;
private ContosoUniversity.Data.TargetContext _targetContext;
public IndexModel(ContosoUniversity.Data.TenantContext context, ContosoUniversity.Data.TargetContext targetContext)
{
_context = context;
//set as default targetcontext -> dummy/empty one.
_targetContext = targetContext;
}
public TenantContext Context => _context;
public TargetContext TargetContext { get => _targetContext; set => _targetContext = value; }
public async Task OnGetAsync()
{
//get data from default target.
var student1 = _targetContext.Students.First();
//or
//switch tenant
//lets say you login and have the users ID as guid.
//then return list of tenants for this user from tenantusers.
var ut = await _context.TenantUser.FindAsync("9245fe4a-d402-451c-b9ed-9c1a04247482");
//now get the tenant(s) for this user.
var SelectedTentant = await _context.Tenants.FindAsync(ut.TenantID);
DbContextOptionsBuilder<TargetContext> Builder = new DbContextOptionsBuilder<TargetContext>();
Builder.UseSqlServer(SelectedTentant.ConnectionString);
_targetContext = new TargetContext(Builder.Options);
//now get data from the switched to database.
var student2 = _targetContext.Students.First();
}
}
Tenant.
public class Tenant
{
public int TenantID { get; set; }
public string Name { get; set; }
//probably could slice up the connenctiing string into props.
public string ConnectionString { get; set; }
public ICollection<TenantUser> TenantUsers { get; set; }
}
TenantUser.
public class TenantUser
{
[Key]
public Guid UserID { get; set; }
public string TenantID { get; set; }
}
Default connstrings.
{ "AllowedHosts": "*",
"ConnectionStrings": {
"TenantContext": "Server=(localdb)\mssqllocaldb;Database=TenantContext;Trusted_Connection=True;MultipleActiveResultSets=true",
"TargetContext": "Server=(localdb)\mssqllocaldb;Database=TargetContext;Trusted_Connection=True;MultipleActiveResultSets=true"
}
I'm trying to add refresh tokens to my existing Web API project. The initial token authentication is based on the example here
This works perfectly as expected
When trying to continue on with adding Refresh Tokens using the follow up tutorial here.
As soon as I get to to the section to add two DbSets to the existing
AuthContext IdentityDbContext authentication stops working
(DefaultConnection is the correct connection string in web.config).
public class AuthContext : IdentityDbContext<IdentityUser>
{
public AuthContext()
: base("DefaultConnection")
{
}
public DbSet<Client> Clients { get; set; }
public DbSet<RefreshToken> RefreshTokens { get; set; }
}
Working back though the code the UserManager.FindAsync method is returning null for the user, even though the login details are exactly as before.
I have checked the UserManger is using Microsoft.AspNet.Identity.Core.2.2.1 and the corresponding tables are in the database.
As soon as I remove the two DbSets the authentication works again, and
trying the sample project included on the tutorials it works fine
Can anyone suggest why this happens and how I could fix it?
Thanks
Mark
First off, I know that this falls into the category of "not recommended practice", but I have a requirement to convert Word documents to PDF, via an ASP.Net site, and I have been using the Word Interop as it is free, simple to implement and Word is already installed on the server.
When I have been testing, it is working for me but after publishing users are reporting "Unauthorised access" errors. I have admin rights to the server and I found that adding the users as admin on the server works but I do not want to grant admin rights to every user.
So one of a number of things needs to happen, is there an alternative, free, library for converting a Word document to PDF that I could be using? Is there an easy way to get my users access to the Interop library without giving admin rights? Is there a way to impersonate an admin user for this part of the web application, seeing as the application requires Windows Authentication?
Is there anything else I have not thought of that could benefit me?
You can use impersonation to run code as a different user. There is a good impersonation class available on CodeProject here, http://www.codeproject.com/Articles/10090/A-small-C-Class-for-impersonating-a-User.
Just create a new admin account on the machine with access to do what you want, store the credentials in the web.config in app settings, (encrypt them if you want to with rijandael and decrypt them with code, etc.
But long store short, you use it like this,
using ( new Impersonator( "myUsername", "myDomainname", "myPassword" ) )
{
//This code is running elevated as the specified user
}
//This code is reverted back to the previous user.
I actually wrote my own web.config ConfigurationElement for storing credentials. It looks like this,
public class CredentialConfigurationElement : ConfigurationElement
{
#region Properties
[ConfigurationProperty("userName", DefaultValue="", IsRequired=true)]
public string UserName
{
get { return (string)this["userName"];}
set { this["userName"] = value; }
}
[ConfigurationProperty("password", DefaultValue = "", IsRequired = true)]
public string Password
{
get { return (string)this["password"]; }
set { this["password"] = value; }
}
#endregion
#region Explicit Operators
public static implicit operator NetworkCredential(CredentialConfigurationElement value)
{
return new NetworkCredential(value.UserName, value.Password);
}
public static implicit operator CredentialConfigurationElement(NetworkCredential value)
{
return new CredentialConfigurationElement() { UserName = value.UserName, Password = value.Password };
}
#endregion
}
However, to use it, you need to make a Custom ConfigurationSection, a class that inherits from ConfigurationSection and exposes the CredentialConfigurationElement as a property.
E.g. you could make a new section called CodeImpersonatorSection : ConfigurationSection
And in there expose the CredentialConfigurationElement as a property called ImpersonationCredentials.
Then use (CodeImpersonatorSection)WebConfigurationManager.GetSection("/yoursectionnamehere"); to get an instance to the configuration.
Optionally modify the Impersonator class to do that automatically, and change it to have a static method like
Impersonator.RunUnderImpersonation(p => {
//This code is impersonating the configured user.
});
My question involves both a technical and a modeling issue.
I have a MCV 5 system where there will be a web front-end and also a mobile app(Android).
At some point the web user will be able to see a queue of mobile signed-on users.
Mobile accounts wont access web front-end neither web accounts the mobile app.
I have chosen Azure to provide all my system needs.
Modeling wise, should I create separate login systems(one more backend system for mobile login)?
If not, is the Asp.net Identity system capable of using two different logins, as I tought roughly something like this:
namespace Application.Models {
// You can add profile data for the user by adding more properties to your ApplicationUser class, please visit http://go.microsoft.com/fwlink/?LinkID=317594 to learn more.
public class ApplicationUser : IdentityUser
{
public virtual ICollection<ApplicationMobileClient> Clients { get; set; }
}
public class ApplicationMobileClient : IdentityUser
{
}
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("DefaultConnection")
{
}
}
}
The web user model would have a collection of mobile clients model. Just like an photo/comments system. But I dont know(well I asume not) if the Identity can do something like this, handle two logins on same application. I could Maybe im just larning too much technologies/frameworks/metodologies/patters at the same time and Im just dont know what to do lol.
I can't see why you should need two different ApplicationUser. Because it's the same user regardless if he is on your website or signed in through your Android app. If you want to keep track of the users mobile sign ins then you can add a property to the ApplicationUser.
public class ApplicationUser : IdentityUser
{
public virtual ICollection<MobileSignIn> MobileSignIns { get; set; }
}
public class MobileSignIn
{
}
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("DefaultConnection")
{ }
}
In my first project going real DDD, I'm stuck with some doubts concerning which course to take in this scenario...
I have a distributed architecture, in which users of several applications will have only one account that will make them able to authenticate. This account can be created on our own system, or the user can share with us his login with facebook, google and other third-party account provider.
So, there's a project for only this purpose, control user accounts.
This scenario made me came with this approach within my model (simplified):
public class User
{
public User(string name)
{
Id = Guid.NewGuid();
Name = name;
}
public Guid Id { get; protected set; }
public string Name { get; protected set; }
}
public abstract class Account
{
protected Account(User user)
{
Id = Guid.NewGuid();
User = user;
}
public Guid Id { get; protected set; }
public User User { get; protected set; }
}
public class MySystemAccount : Account
{
public MySystemAccount(string email, string password, User user)
: base(user)
{
Email = email;
Password = password;
}
public string Email { get; protected set; }
public string Password { get; protected set; }
}
public class FacebookAccount : Account
{
public FacebookAccount(string facebookId, User user)
: base(user)
{
FacebookId = facebookId;
}
public string FacebookId { get; protected set; }
}
The thing is that the other applications will access this project via REST services.
So, I thought about a single /Authenticate service that will provide a json with dynamic form. It could de a json with a email/password, or a json with the facebookId.
But then, how can I connect the layers?
I thought about making an application service, but I got stuck on who and how should decide what is going on, what should my rest service communicate to the application and how the application will know to do the thing, whatever kind of authentication it is, an user from my own domain or a user from facebook and so on..
Any thoughts on this?
Thanks!
This seems to be a multi-part question - one part about the object model and polymorphism and another about architecture.
Regarding the object model, the use of inheritance isn't ideal in this scenario. Each sub-type of Account won't really have much specific behavior or any behavior at all. The only specialization is the presence of different data fields. Additionally, use of inheritance will complicate persistence.
Architecturally, what I think you're trying to achieve is federated identity. This basically decouples the notion of a user (an identity) from the authentication process. In turn, this allows all remaining application code to bypass authentication concerns and depend only on the user's identity. Take a look at OpenID as well as the DotNetOpenAuth library which provides an OpenID implementation in C#.
I'm new to Stackoverflow, so not sure how to just put this as a "suggestion", but I would rethink your model a little bit. I think of a "User" as someone who strictly is a person utilizing your application through your own website. This "User" would go through the authentication as you suggested, either via an account from your own system, or via an Open ID or OAuth ID provider like Facebook and Google.
If an application however, wants to access your "application" via REST calls, then I'd put them through a different authentication mechanism. In a sense to me, you are providing an API layer and software as a service. I'd take a look at how Twitter, Facebook, or Google expose their APIs for other applications to use. Typically, there is a secret key and application ID involved in authenticating the REST calls.