I have a multi-tenant system where each tenant shares the same instance of the codebase, but has their own databases.
I'm using RavenDB for persistence, with a standard c# facade/BLL backend wrapped with Asp.net WebAPI, and I'm finding that at every lower level operation (deep within my business logic classes) that touch the datbase, I need to pass in an identifier so that my RavenDb client session knows which database to operate against.
When the user authenticates, I resolve the appropriate database identifer, store it in the session manager. Every call against the Web API layer passes in a session ID which resolves the database ID in the backend, which is then used to pass into every single facade/BLL call.
All my dependencies are handled via an IoC container at the WebAPI level, but i can't pass in the database ID at this phase because it can be different for every user that is logged in.
this, of course is getting tedious.
can someone give me some guidance as to what I can do to alleviate this? Maybe perhaps some sort of policy injection/AOP solution?
a rough sample of my backend code looks like..
public class WidgetService()
{
private WidgetBLL _widgetBLL;
private ISessionManager _sessionManager;
public WidgetService(WidgetBLL _widgetBLL, ISessionManager sessionManager)
{
_widgetBLL = widgetBLL;
_sessionManager = sessionManager
}
public Widget getWidget(string sessionId, string widgetId)
{
string DbId = sessionManager.ResolveDbId(sessionId)
return _widgetBLL.GetWidget(string dbId, string widgetId);
}
}
public class WidgetManager()
{
public GetWidget(string dbId, string widgetId)
{
using (IDocumentSession session = documentStore.OpenSession(dbId)
{
var widget = session.load<Widget>(widgetid);
}
return widget;
}
}
the DBID is the identifier for that particular tenant that this particular user is a member of.
You need to change how you are using the session.
Instead of opening and closing the session yourself, do that in the IoC code.
Then you pass a session that is already opened for the right db.
Related
In a Net Core 5 Web API application, I need to change the connection string of a secondary dbcontext with information obtained from the main dbcontext
There are two dbcontext:
DbContextMaster - static connection string
DbContextChild - dynamic connection string. Use the template in appsettings like this:
"DbContextChild": "Data Source=server;Initial Catalog={dbName};Integrated Security=False;User Id=username;Password="
When starting the application:
I get a header parameter to identify the tenant (so far ok)
I need to perform a query with this parameter using a DbContextMaster to get the database name. If the value of the header parameter identifying the tenant DOES NOT exist in this database, it must return badRequest, if it exists, must change DbContextChild template with database name from tenant, so that only then does the controller getting the data from the DbContext Child respond to the requests.
I have many questions about how to carry out such an implementation. Is there any way to intercept the call from all controllers by doing the above checks? Or would it be necessary to have a base controller and everyone else inherit it?
I'm trying the built-in Authentication of ServiceStack. My approach is 'OrmLiteAuthRepository' so users' information are stored in Sql Server instead of the default in memory storage. I use Postman to test the endpoints.
My target is receiving user rows, updating user information, creating users, deleting an user row. Those are the endpoints I found in Postman after importing (I didn't create those endpoints):
GET 'http://localhost:47391/api/register',
PUT 'http://localhost:47391/api/json/reply/Register'
POST 'http://localhost:47391/api/json/reply/Register'
I tested POST, Sql Server automatically created the tables to store user data. And the data could be written into Sql Server so I have no problem with POST.
But with PUT, isn't it for updating the existing row? I append '/{id}' to the end. But it created a new row in the database instead of updating the existing one. How does it work?
With GET, I got no implementation error.
{
"ResponseStatus": {
"ErrorCode": "NotImplementedException",
"Message": "Could not find method named Get(Register) or Any(Register) on Service RegisterService",
"StackTrace": " at ServiceStack.Host.ServiceExec`1.Execute(IRequest request, Object instance, Object requestDto, String requestName)\r\n at ServiceStack.Host.ServiceRequestExec`2.Execute(IRequest requestContext, Object instance, Object request)\r\n at ServiceStack.Host.ServiceController.<>c__DisplayClass11.<>c__DisplayClass13.<RegisterServiceExecutor>b__10(IRequest reqCtx, Object req)\r\n at ServiceStack.Host.ServiceController.ManagedServiceExec(ServiceExecFn serviceExec, IService service, IRequest request, Object requestDto)\r\n at ServiceStack.Host.ServiceController.<>c__DisplayClass11.<RegisterServiceExecutor>b__f(IRequest requestContext, Object dto)\r\n at ServiceStack.Host.ServiceController.Execute(Object requestDto, IRequest req)\r\n at ServiceStack.HostContext.ExecuteService(Object request, IRequest httpReq)\r\n at ServiceStack.Host.RestHandler.GetResponse(IRequest request, Object requestDto)\r\n at ServiceStack.Host.RestHandler.ProcessRequestAsync(IRequest httpReq, IResponse httpRes, String operationName)"
}
}
How to implement it? I assume I consider the user a normal Web Service entity? and create 'UserService', and requests like:
[Route("/register")]
public class User : IReturn<UserResponse>
{
...
}
BUT there isn't a model class like 'User' due to the tables are created by ServiceStack itself, how to solve this?
Or is there something I am not aware of. Thanks.
The error message:
Could not find method named Get(Register) or Any(Register) on Service RegisterService
Is saying you're trying to call the built-in ServiceStack Register Service instead of your Service. But the Register Services isn't enabled by default, your AuthFeature likely explicitly enables it, either with:
Plugins.Add(new RegistrationFeature());
Or on the AuthFeature:
Plugins.Add(new AuthFeature(...) {
IncludeRegistrationService = true
});
If you don't want to enable ServiceStack's built-in Register Service you'll need to remove the registration where it's enabled.
If you instead want the Register Service registered at a different path, you can specify a different route with:
Plugins.Add(new RegistrationFeature {
AtRestPath = "/servicestack-register"
});
I'm using jboss as 7.1.1.Final and have configured a security-domain using LdapExtLoginModule. The login works so far.
I now want to access the roles loaded by the module in an SLSB. I know how to access the username. My example uses ejb 3.1 and prints the username to System.out.
I don't know how to access the roles and didn't find anything in the documentation. The EJBContext provides the method isCallerInRole(String) which proves that the context itself knows the roles but I can not find a method which returns a set of roles.
I know that I could write a custom LoginModule which extends the LdapExtLoginModule and sets a custom principal containing the roles. But maybe there is an easier approach using existing functionality. Does anybody know such approach?
SLSB code:
#Stateless
#Remote(IAService.class)
public class AService implements IAService
{
#Resource
private EJBContext context;
#Override
public void printUserData() {
System.out.println("Name: " + context.getCallerPrincipal().getName());
// TODO print roles
}
}
Nobody answered my question so far and because I finally have a working solution I will now share it with you:
The Java EE API does not provide any methods to access the information in server independent way. So I tried the method of writing a login module which sets a custom principal. As it turns out this does not work either.
Inspired by the answer to this question I now use the following code to get the caller roles:
private Group getRoles() {
final Subject subject = (Subject)PolicyContext.getContext("javax.security.auth.Subject.container");
final Set<Group> groups = subject.getPrincipals(Group.class);
for (final Group group : groups) {
if ("Roles".equals(group.getName())) {
return group;
}
}
throw new IllegalStateException("No roles group found");
}
The Group returned by this method contains Principals which have the names of the users groups. This only works on JBoss whose LoginModules set a Group of name "Roles".
We are using spring security in our web application based on spring MVC.
We are doing authentication using LDAP module of spring security which is working properly. Now I need to get the user password from LDAP for saving in the database.
For this I am using this in my code.
public class PersonContextMapper implements UserDetailsContextMapper {
public UserDetails mapUserFromContext(DirContextOperations ctx, String username, Collection<GrantedAuthority> authorities) {
Person.Essence p = new Person.Essence(ctx);
p.setUsername(username);
p.setAuthorities(authorities);
Object passwordValue = ctx.getObjectAttribute("userPassword");
return p.createUserDetails();
}
public void mapUserToContext(UserDetails user, DirContextAdapter ctx) {
Assert.isInstanceOf(Person.class, user, "UserDetails must be a Person instance");
Person p = (Person) user;
p.populateContext(ctx);
}
}
But I am not getting the any value for the password. Its always null.
Please help.
PS. My authentication is successful. It means password entered in the login form is matches properly with the password stored in the LDAP.
It might be that the authentication state of the connection does not have permission to read the value of the userPassword attribute. Most often, applications issue a BIND request to the directory server, including appropriate controls as necessary. The password is included in the BIND request and the directory server changes the authentication state of the connection upon successful completion of the BIND request. In any case, the value of the userPassword attribute is encrypted or hashed more often than not, and applications have no need to read the value.
We have audit columns set by triggers.
For obscure security reasons predating my tenure and out of my control, we log in with a generic user, and do a 'set session authorization' to change the user to the db user of the user who is logged in.
When we converted to NHibernate, it creates a whole new session and jacks everything up
when we try to do a set session auth, so we turned the set session auth off...
Now we are trying to find out a way to get NHibernate to let us do 'set session authorization' without recycling the session on us, so we can use our existing trigger based audit column stuff with both legacy apps, and our new NHibernate apps.
It's not a ideal soloution, or the best way to do it even, but is it possible?
I was hoping there was a alternate interface that allowed this kind of access.
Does anyone know how to do it, or can you point me towards and good hints?
Thanks,
Eric-
You can inherit DriverConnectionProvider and do whatever you need when creating a connection.
Example:
public class MyConnectionProvider : DriverConnectionProvider
{
public override IDbConnection GetConnection()
{
var connection = base.GetConnection();
var sessionAuthCommand = connection.CreateCommand();
sessionAuthCommand.CommandText = "set session authorization " + GetUser();
sessionAuthCommand.ExecuteNonQuery();
return connection;
}
}
And then configure NHiberate to use that as the connection provider.
(GetUser is the method where you'll provide the correct user)