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)
Related
I already read the multi-tenancy guide published, and I believe the solution I require for my app would be to create a separate DB Connection for each organization that I sign up.
My issue is that, since I'm going to be setting the connection parameter to a different name per client, I would have liked Universal Login to automatically determine the DB Connection name according to the user's email address. So, instead of the user manually providing some kind of a hint to which DB Connection I should authenticate them against, I would like to automatically determine that somehow.
Is there any way to do this?
I am assuming that you are using hosted login page. The easiest way to determine the connection based on client would be to pass the connection parameter when redirecting to /authorize endpoint. Thus, Lock will use the connection parameter passed in the URL as the connection to validate the user. For example:
https://[tenant]/authorize?
client_id=K8B5DJdStcZtUzbhaxAOzCrXNbo2kmXG&
response_type=token%20id_token&
redirect_uri=http://application_url&
scope=openid%20profile%20email%20&
connection=connection_name&state=123&nonce=345
Both auth0.js and auth0-spa-js can be used to pass the extra parameter(connection).
Second approach would be to use connectionResolver options if you are using Hosted Login Page+ Lock .
connectionResolver {Function}: When in use, provides an extensibility point to make it possible to choose which connection to use based on the username information. Has username, context, and callback as parameters. The callback expects an object like: {type: 'database', name: 'connection name'}.
var options = {
connectionResolver: function (username, context, cb) {
var domain = username.includes('#') && username.split('#')[1];
if (domain) {
// If the username is test#auth0.com, the connection used will be the `auth0.com` connection.
// Make sure you have a database connection with the name `auth0.com`.
cb({ type: 'database', name: domain });
} else {
// Use the default approach to figure it out the connection
cb(null);
}
}
}
Instead of username, you can take advantage of context object to identify the client (context.clientID) and choose the connection.
We use Apache Shiro to authenticate and authorize users using our active directory.
Authenticating the user and mapping groups works just fine using the following config:
adRealm = org.apache.shiro.realm.activedirectory.ActiveDirectoryRealm
adRealm.searchBase = "OU=MYORGANIZATION,DC=MYDOMAIN,DC=COM"
adRealm.groupRolesMap = "CN=SOMEREADGROUP":"read","CN=SOMEMODIFYGROUP":"modify","CN=SOMEADMINGROUP":"admin"
adRealm.url = ldaps://my.ad.url:636
adRealm.systemUsername= systemuser
adRealm.systemPassword= secret
adRealm.principalSuffix= #myorganization.mydomain.com
I can authenticate in Shiro using the following lines:
String user = "someuser";
String password = "somepassword";
Subject currentUser = SecurityUtils.getSubject ();
if (!currentUser.isAuthenticated ()){
UsernamePasswordToken token = new UsernamePasswordToken (user,
password);
token.setRememberMe (true);
currentUser.login (token);
}
We now want to get more user information from our ActiveDirectory. How can I do that using Apache Shiro? I was not able to find anything about it in the documentation.
In the source code of ActiveDirectoryRealm I found this line:
NamingEnumeration answer = ldapContext.search(searchBase, searchFilter, searchArguments, searchCtls);
So the first part of the answer is clear: use the ldapContext to search something in it. But how can I retrieve the LdapContext?
It depends on what you are trying to do. Are you just trying to reuse the context to run a query for something other then authentication or authorization? Or are you trying to change the behavior of the query in the AD realm?
If the latter, you would need to extend the ActiveDirectoryRealm and override the queryForAuthorizationInfo() method.
Are you implementing something that is custom for your environment?
(updated)
A couple things:
The realm has access to the LdapContext in the two touch points: queryForAuthenticationInfo() and queryForAuthorizationInfo(), so if you extend the AD realm or AbstractLdapRealm you should already have it. You could change the query to return other info and add the extra info to your Principal. Then you have access to that info directly from your Subject object.
Your realms, are not required to be singletons.
If you want to do some other sort of user management (email all users with a given role, create a user, etc). Then you could create a LdapContextFactory in your shiro.ini, and use the same instance for multiple objects.
[main]
...
ldapContextFactory = org.apache.shiro.realm.ldap.JndiLdapContextFactory
ldapContextFactory.systemUsername = foobar
ldapContextFactory.systemPassword = barfoo
adRealm = org.apache.shiro.realm.activedirectory.ActiveDirectoryRealm
adRealm.ldapContextFactory = $ldapContextFactory
...
myObject = com.biz.myco.MyObject
myObject.ldapContextFactory = $ldapContextFactory
This would work well if myObject is interacting with other Shiro components, (responding to events, etc), but less so if you need access to it from another framework. You could work around this by some sort of static initialization that builds creates the ldapContextFactory, but in my opinion, this is where the sweet spot of using the shiro.ini ends, and where using Guice or Spring shines.
Problem. In a registration scenario, I'm trying to insert a user in my User table and then call WebSercurity.CreateAccount for that user (in a transaction). This causes the error that MS DTC is not available on the server.
Description. The reason I'm doing this is because I have a Customer Entity which inherits from User, so WebSercurity.CreateUserAndAccount cannot be used because it doesn't know about Customer and just inserts a User record.
I'm using Asp.net MVC 4 with EntityFramework 5, CodeFirst, and SQL Server 2008 R2.
any suggestions for not using DTC would be appreciated!
EDIT.
It is obvious why this error occurs, because websecurity uses its own connection to the database, and my repositories use another connection, although I've configured simplemembership to use the same DbContext class as my repositories, but the problem is it creates a new instance of the DbContext ...
I was hoping if there is a way to pass an existing context object, or connection to the WebSecurity to use with its methods.
here's the code:
if (ModelState.IsValid)
{
//using (TransactionScope tx = new TransactionScope())
//{
UnitOfWork.UserRepository.Insert(new Customer
{
FirstName = model.FirstName,
LastName = model.LastName,
Email = model.Email,
Tel = model.Tel,
Mobile = model.Mobile,
BirthDate = model.BirthDate,
InsertDate = DateTime.Now,
UserType = UserType.Customer,
MaritalStatus = model.MaritalStatus,
ZipCode = model.ZipCode,
StreetAddress = model.StreetAddress,
City = model.City,
State = model.State
});
UnitOfWork.Commit();
string token = WebSecurity.CreateAccount(model.Email, model.Password, true);
Roles.AddUserToRole(model.Email, "Customer");
//WebSecurity.Login(model.Email, model.Password, true);
await Task.Run(() => EmailHelper.SendConfrimationEmail(token, model.Email));
// tx.Complete();
//}
The DTC error occurs because you are trying to span a transaction over two different database connections. You have several options.
SimpleMembership is designed for simple scenarios. You are doing an advanced scenario, so you should probably use a different membership provider.
I found a possible solution but I'm not sure if it's the best solution. The idea came from this blog post that says how we can include simplemembership tables in our POCO entities and create the tables ourselves (not using WebSecurity).
So as a result I think I can implement the CreateAccount method in my repositories by simply inserting a record in the webpages_Membership table, and AddUserToRole by inserting a record in webpages_UsersInRoles table.
For other queries like GetUser and ... we can use WebSecurity and Roles class like before.
It seems to work OK (otherwise I'm missing something), but has some extra work to do which I wish not to!
So if anyone can give me a better solution I would be glad.
As Mystere Man pointed out the error is occurring because the transaction is over two different databases. You have several options. Here are a few that come to mind.
You can customize the existing User entity used by the SimpleMembership provider to capture the custom information you want for each user. This is fairly straightforward and is discussed in this blog post. It looks like you are trying to do an email confirmation which the SimpleMembership provider also supports and is discussed here.
You can also tie the information in your other database with the unique user ID (int) that SimpleMembership uses. Once you create the user and log them in you can get that id by calling WebSecurity.CurrentUserId.
You could bypass the whole SimpleMempership provider and create your own custom membership provider, which is discussed here.
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.
My problem is I have existing user in database which store the key and secret from the first authentication. I wish to reuse it again when I come back. For the first time authentication, everything working fine. I can use every method call from Dropbox API and the Token(key and secret) was stored in database.
I come back to app and get the Token from database, set it to the session, link current session with API.
session = new WebAuthSession(appKeys, ACCESS_TYPE);
api = new DropboxAPI<WebAuthSession>(session);
String userKey = dropboxUserObj.getUserKey(); //Key from database
String userSecret = dropboxUserObj.getUserSecret();//Secret from database
AccessTokenPair userAccessTokenPair = new AccessTokenPair(userKey, userSecret);
session.setAccessTokenPair(userAccessTokenPair);
It return DropboxUnlinkedException to me when I want to get user data from api using
String userDisplayName = api.accountInfo().displayname;
I have checked on debug mode. Api was linked with the current session. The current session stored Appkey and user's token and correct access type. The point that I doubt is I saw "client = null". I maybe forgot something but I check them all, try every possibilities I can think of but it still return me "DropboxUnlinkedException" which mean I haven't set an access token pair on the session and I didn't revoke access for sure.
Please help me figure out...
I added a screenshot maybe it can illustrate my problem