How to clear cache for a subject in Shiro - permissions

Shiro provides cache feature but in my case, I'm using dynamic roles and permissions for the users. I need to expire the cache for a particular user if any so that changes in permissions immediately affect to user.
There is a method in Realm, but how I get an instance of associated realm to invoke method to clear cache.

I ended up exposing the private method "clearCachedAuthorizationInfo" in the extended Realm of AuthorizingRealm. Then just pass in the principals.
public class MyRealm extends AuthorizingRealm {
//...
#Override
public void clearCachedAuthorizationInfo(PrincipalCollection principals)
{
super.clearCachedAuthorizationInfo(principals);
}
//...
}
to clear the authorization cache:
realm.clearCachedAuthorizationInfo( SecurityUtils.getSubject().getPrincipals() );
I think this is a bit cleaner/safer because this method has additional checks against null on the cache and will ensure you get a reference to the cache if one exists. Simply calling getAuthorizationCache() doesn't do this and may or may not work all the time.
You do need to maintain a reference to the realm. I did this by initializing Shiro via Spring and then injecting it as a Singleton bean wherever I needed it.

If you check out the source of the method getAuthorizationInfo in AuthorizingRealm, you see it is simply using a key/value store to cache the authorization info.
It uses the PrincipalCollection object as a key.
So if you call something like:
realm.getAuthorizationCache().remove(SecurityUtils.getSubject().getPrincipals())
the cache should normally be cleared.

Related

access ResourceInfo in Quarkus / Vertx HttpAuthenticationMechanism

In Quarkus (resteasy reactive), is there a way to get hold of the "ResourceInfo" in an HTTP Authentication Mechanism?
What I'm trying to do is read an annotation that is defined on the resource class or method, in order to choose an authentication mechanism based on it.
Injecting the ResourceInfo directly in the mechanism class does not work (and also, it is application scoped and not request scoped, so not sure it could work). I also couldn't find the info I need in the RoutingContext parameter.
I have also tried adding a ContainerRequestFilter, in which injecting the ResourceInfo with #Context works well, but I think perhaps the filters are called after the httpAuthenticationMechanism.authenticate(), because it's not called in my test when the endpoint requires authentication.
Is there another way to do this?
----> To clarify with code what I would like to do:
have different JAX-RS resources with a custom #Authorization annotations with different "api names" like this:
#Path("/jwttest")
#ApplicationScoped
#Authorization("jwttest")
public class JWTTestController {
...
}
#Path("/oidctest")
#ApplicationScoped
#Authorization("myoidc")
public class OIDCTestController {
...
}
and then different configs like this:
myframework.auth.jwttest.type=jwt
myframework.auth.jwttest.issuer=123
myframework.auth.jwttest.audience=456
myframework.auth.myoidc.type=oidc
myframework.auth.myoidc.auth-server-url=myurl
And in the HttpAuthenticationMechanism, find the value of #Authorization, and based on it, call another provider like suggested in https://quarkus.io/guides/security-customization#dealing-with-more-than-one-httpauthenticationmechanism with the right api name so that it can load the config.

Autofac Multitenant Database Configuration

I have a base abstract context which has a couple hundred shared objects, and then 2 "implementation" contexts which both inherit from the base and are designed to be used by different tenants in a .net core application. A tenant object is injected into the constructor for OnConfiguring to pick up which connection string to use.
public abstract class BaseContext : DbContext
{
protected readonly AppTenant Tenant;
protected BaseContext (AppTenant tenant)
{
Tenant = tenant;
}
}
public TenantOneContext : BaseContext
{
public TenantOneContext(AppTenant tenant)
: base(tenant)
{
}
}
In startup.cs, I register the DbContexts like this:
services.AddDbContext<TenantOneContext>();
services.AddDbContext<TenantTwoContext>();
Then using the autofac container and th Multitenant package, I register tenant specific contexts like this:
IContainer container = builder.Build();
MultitenantContainer mtc = new MultitenantContainer(container.Resolve<ITenantIdentificationStrategy>(), container);
mtc.ConfigureTenant("1", config =>
{
config.RegisterType<TenantOneContext>().AsSelf().As<BaseContext>();
});
mtc.ConfigureTenant("2", config =>
{
config.RegisterType<TenantTwoContext>().AsSelf().As<BaseContext>();
});
Startup.ApplicationContainer = mtc;
return new AutofacServiceProvider(mtc);
My service layers are designed around the BaseContext being injected for reuse where possible, and then services which require specific functionality use the TenantContexts.
public BusinessService
{
private readonly BaseContext _baseContext;
public BusinessService(BaseContext context)
{
_baseContext = context;
}
}
In the above service at runtime, I get an exception "No constructors on type 'BaseContext' can be found with the constructor finder 'Autofac.Core.Activators.Reflection.DefaultConstructorFinder'". I'm not sure why this is broken....the AppTenant is definitely created as I can inject it other places successfully. I can make it work if I add an extra registration:
builder.RegisterType<TenantOneContext>().AsSelf().As<BaseContext>();
I don't understand why the above registration is required for the tenant container registrations to work. This seems broken to me; in structuremap (Saaskit) I was able to do this without adding an extra registration, and I assumed using the built in AddDbContext registrations would take care of creating a default registration for the containers to overwrite. Am I missing something here or is this possibly a bug in the multitenat functionality of autofac?
UPDATE:
Here is fully runable repo of the question: https://github.com/danjohnso/testapp
Why is line 66 of Startup.cs needed if I have lines 53/54 and lines 82-90?
As I expected your problem has nothing to do with multitenancy as such. You've implemented it almost entirely correctly, and you're right, you do not need that additional registration, and, btw, these two (below) too because you register them in tenant's scopes a bit later:
services.AddDbContext<TenantOneContext>();
services.AddDbContext<TenantTwoContext>();
So, you've made only one very small but very important mistake in TenantIdentitifcationStrategy implementation. Let's walk through how you create container - this is mainly for other people who may run into this problem as well. I'll mention only relevant parts.
First, TenantIdentitifcationStrategy gets registered in a container along with other stuff. Since there's no explicit specification of lifetime scope it is registered as InstancePerDependency() by default - but that does not really matter as you'll see. Next, "standard" IContainer gets created by autofac's buider.Build(). Next step in this process is to create MultitenantContainer, which takes an instance of ITenantIdentitifcationStrategy. This means that MultitenantContainer and its captive dependency - ITenantIdentitifcationStrategy - will be singletons regardless of how ITenantIdentitifcationStrategy is registered in container. In your case it gets resolved from that standard "root" container in order to manage its dependencies - well, this is what autofac is for anyways. Everything is fine with this approach in general, but this is where your problem actually begins. When autofac resolves this instance it does exactly what it is expected to do - injects all the dependencies into TenantIdentitifcationStrategy's constructor including IHttpContextAccessor. So, right there in the constructor you grab an instance of IHttpContext from that context accessor and store it for using in tenant resolution process - and this is a fatal mistake: there's no http request at this time, and since TenantIdentitifcationStrategy is a singleton it means that there will not ever be one for it! So, it gets null request context for the whole application lifespan. This effectively means that TenantIdentitifcationStrategy will not be able to resolve tenant identifier based on http requests - because it does not actually analyze them. Consequently, MultitenantContainer will not be able to resolve any tenant-specific services.
Now when the problem is clear, its solution is obvious and trivial - just move fetching of request context context = _httpContextAccessor.HttpContext to TryIdentifyTenant() method. It gets called in the proper context and will be able to access request context and analyze it.
PS. This digging has been highly educational for me since I had absolutely no idea about autofac's multi-tenant concept, so thank you very much for such an interesting question! :)
PPS. And one more thing: this question is just a perfect example of how important well prepared example is. You provided very good example. Without it no one would be able to figure out what the problem is since the most important part of it was not presented in the question - and sometimes you just don't know where this part actually is...

Detecting actual login in Spring Security filter chain

For a non-trivial setup involving session concurrency control, authentication is not equivalent to login. For example, a user can be authenticated successfully but still redirected to a logout or error page if this is a second login attempt and max-sessions="1". If I have post-login logic that needs to be invoked at the point of login (not authentication), what is an optimal way to incorporate this logic into a Spring Security-based webapp? The solution I came up with, based on my limited understanding of the framework, was to implement my own custom ConcurrentSessionControlAuthenticationStrategy adapter extending the framework's ConcurrentSessionControlAuthenticationStrategy and inject it into CompositeSessionAuthenticationStrategy in my Spring Security configuration XML. I created a 1-arg constructor and onAuthentication method. onAuthentication performs my post-login processing before calling super.onAuthentication. Is there a better way to do this?
My custom onAuthentication method looks something like
.
.
if (sessionCount < allowedSessions) {
// Record login timestamp in database
Date now = new Date();
userDao.setLastLogin(now);
userDao.save();
}
super.onAuthentication(...);
Because sessionRegistry member variable is declared private (as opposed to protected) in the parent, I had to declare my own sessionRegistry and initialize it inside the 1-arg constructor so that my onAuthentication method would have access to it.

Obtain servlet user credentials (password) after login, e.g. from j_security_check

I need to access the user's password in a Jetty application after authentication, and can't figure out how.
I am migrating a set of web pages away from basic authentication in an embedded Jetty servlet application. I cannot, however, completely remove basic authentication from all of the services that these pages call, so I need to forward the user credentials in some cases, which means storing and later retrieving the user's password.
I introduced forms authentication to the root context via the standard markup in web.xml, which works fine but I can find no way of getting the user credentials programatically. As far as I can tell there is no way to place a Filter on j_security_check to intercept and store the form parameters in the session state. Jetty provides all the user credentials in the session state but this is in a container-specific key and although the application is currently tied to Jetty I would strongly prefer a container-agnostic solution. Here are some specific questions that I've tried to formulate after going down a number of seemingly dead-end streets:
How can I obtain the user's password after login? Even if I moved the services away from basic authentication I would still need to perform some secondary action such as obtaining a token, in which case I would still need their credentials under my control for a brief period.
Assuming I can't obtain the user's password directly, perhaps I can leverage something in the container to store the credentials. I can implement my own LoginService that wraps the actual implementation, but that class does not have access to the HttpSession or Request objects. Where is the proper place to do this?
If I need to implement a custom login solution, I'm not quite sure where to start...the FormAuthenticator has a lot of complicated session state management that I would like to preserve and not reproduce willy-nilly, and I would still prefer to defer to the container standard as much as possible. Is there some standard method for extending or overriding the j_security_check logic?
I finally found one solution, for anyone else attempting similar - and I've found quite a few other posts from confused devs, and some badly hacked together workarounds. I believe this one is correct, although you must do the URL filtering yourself and it leaves open the question as to why this is possible, if indeed j_security_check should be exempt from this type of interception for security reasons, as is claimed many places online. Perhaps I am merely exploiting an unknown gap in the Jetty security measures, so I will leave this question open for a while to see if there is a more robust or explicit solution.
ServletRequestListener allows you to latch onto the j_security_check post request before it is fully initialized. There you can get both the form parameters and the session object. So in the end it was just a matter of exhausting every possible servlet-related class in Jetty until I found one that would give me access to the j_security_check request. Some code:
public class PreAuthenticationRequestListener implements ServletRequestListener {
public static final String USERNAME_KEY = "USERNAME";
public static final String PASSWORD_KEY = "PASSWORD";
#Override
public void requestDestroyed(ServletRequestEvent sre) {
}
#Override
public void requestInitialized(ServletRequestEvent sre) {
HttpServletRequest request = (HttpServletRequest)sre.getServletRequest();
if (request.getRequestURI().contains("j_security_check")) {
final String username = request.getParameter("j_username");
final String password = request.getParameter("j_password");
HttpSession session = request.getSession();
session.setAttribute(USERNAME_KEY, username);
session.setAttribute(PASSWORD_KEY, password);
}
}
}

JEE6 application session scoped objects

I'm still pretty new to JEE6 having come from a Servlets + JSP style of development on legacy systems. In the applications I worked on we would just throw objects into the various supplied scopes (request, session and application) keyed on a constant String. For example, the User object that represented the currently logged in user would be in the session scope keyed under "current_user".
I've done the same in our new JEE6 application, when the user logs in the User object is bound into the session scope. I'm wondering though if there is a better, more EE, way of handling this?
The problem I'm having is that now I've got the User stored in the session it's awkward to get access to it again. I can get it via JNDI look up or with a few lines of boiler plate code involving FacesContext but neither are very elegant.
Rather than boiler plate code all over the place (the User object is need in a few places) it would be great if I could just get the object injected into a field or method. After all there can only be one object in the session bound to a particular name so there shouldn't be any ambiguity about what I'm asking for. Is this possible?
Maybe the CDI could be of any help?
Could you define the way you achieve the User object into one, main method?
If so, and you're working with Java EE 6 environment, you could use the Producer method. Something between these lines:
public class ClassWhichCanAccessUserObject {
#Produces
public User produceUser() {
User u = ... // get the user as you do it right now
return u;
}
}
Then in the place you want to use this class you just Inject it (in the field or method) and use it:
public class MyLogic {
#Inject
User u;
}
You need to remember to add the beans.xml file to your classpath, as without the CDI will not work for your module.