We're using Spring Security #PreAuthorize annotation to check proper roles and permissions of Jersey-based REST APIs. What we're worried about is that we forget the annotation from some method, leaving it unsecured.
Is there a way to ensure that all APIs have the annotation, even if it is just #PreAuthorize("permitAll") (i.e. explicitly permitting everything)?
We're envisioning that if an API does not use the annotation, either the request is blocked (so that the API cannot be used) an error is logged (so that we notice the mistake quickly).
One way would be to make a custom expression that adds a request attribute marking the request as "secured". Then a web filter can check that the request attribute is in place and log an error if it is not.
Is there any cleaner way to do this? For example set "denyAll" as the default policy for un-annotated methods, and require overriding it as appropriate?
I finally resolved this by adding a Jersey ResourceFilterFactory which checks that each method or class has the annotation:
public class RestFilterFactory implements ResourceFilterFactory {
private static final Logger log = LoggerFactory.getLogger(RestFilterFactory.class);
#Override
public List<ResourceFilter> create(AbstractMethod method) {
ensurePreAuthorizedAnnotation(method);
return Arrays.emptyList();
}
private void ensurePreAuthorizedAnnotation(AbstractMethod method) {
boolean found = false;
if (hasPreAuthenticatedAnnotation(method)) {
found = true;
}
if (hasPreAuthenticatedAnnotation(method.getResource().getResourceClass())) {
found = true;
}
if (!found) {
log.error("Resource is not protected with #PreAuthorize annotation: " + method);
}
}
private boolean hasPreAuthenticatedAnnotation(AnnotatedElement element) {
Annotation a = element.getAnnotation(PreAuthorize.class);
return a != null;
}
}
Related
Below code return Mono from ReactiveSecurityContextHolder
#Component
public class SomeClass {
public static Mono<String> issuesID() {
return ReactiveSecurityContextHolder.getContext().switchIfEmpty(Mono.empty()).flatMap((securityContext) -> {
Authentication authentication = securityContext.getAuthentication();
Jwt jwt = (Jwt) authentication.getPrincipal();
String issuer = (String) jwt.getClaims().get("some_claim");
log.info("{}",issuer);
return Mono.justOrEmpty(issuer);
}).switchIfEmpty(Mono.empty());
}
}
I need something like this
String mutatedId = PREXIX+SomeClass.issuesID();
If i do followings,
PREXIX+SomeClass.issuesID().block();
PREXIX+SomeClass.issuesID().subscribeOn(Schedulers.elastic()).block();
PREXIX+SomeClass.issuesID().toProcessor().block();
PREXIX+SomeClass.issuesID().toFuture().get();
They all give the same error.
block()/blockFirst()/blockLast() are blocking, which is not supported in thread rector-xxx
I have also tried delaying the Mono but that's also not helping.
I know reactor is a non-blocking framework and blocking is discourages but in my case I can't figure out another way to solve this.
I need to pass the mutatedId to mongoFactory so i can switch databases based on Jwt property per request. I do not want to inject #AuthenticationPrincipal in controller and keep passing it to downward layers and finally make decision at DOA layer.
#Override
public Mono<MongoDatabase> getMongoDatabase(String dbName) throws DataAccessException {
String mutatedId = PREXIX+SomeClass.issuesID();
return super.getMongoDatabase(mutatedId+"#"+dbName);
}
Any suggestion how this can be achieved or is there any better approach.
You use for instance flatMap.
Mono<MongoDatabase> db = issuesID()
.flatMap(id -> getMongoDatabase(id));
Looking to add the AuthorizeFilterAttribute or AnonymousFilterAttribute to an endpoint in Swashbuckle's implementation of Swagger so I can see which attribute is used on each endpoint in the generated documentation file in a running webapi that ends in /swagger. Is this currenlty possible?
I specifically would like to add a big bold label that says this endpoint is [Anonymous] or that endpoint is using [Authorize] and have them look differently that the summary or remark text.
Also I would like to be able to filter out all the different types of these restriction filter attributes for each endpoint including [NonAction], [Authorize], and [Anonymous] where one of these might be at the top of each controller endpoint. Maybe even eventually add other types of FilterAttributes besides these on each endpoint.
Currently it looks like only the HTTP Methods, the request and response objects can be retrieved in the current implementation so I was not able to find definitive information on this.
Since this is a Swagger implementation do these .NET specific attribute filters not translate to Swashbuckle b/c they only implement what's in the Swagger specification and nothing else?
Finally are their .NET specific extensions to Swashbuckle's implementation that do this?
Thanks!
For the part adding the label to unprotected methods/actions you could use an operation filter like this
public class UnprotectedOperationFilter : IOperationFilter
{
private bool HasAttribute(MethodInfo methodInfo, Type type, bool inherit)
{
// inhertit = true also checks inherited attributes
var actionAttributes = methodInfo.GetCustomAttributes(inherit);
var controllerAttributes = methodInfo.DeclaringType.GetTypeInfo().GetCustomAttributes(inherit);
var actionAndControllerAttributes = actionAttributes.Union(controllerAttributes);
return actionAndControllerAttributes.Any(attr => attr.GetType() == type);
}
public void Apply(Operation operation, OperationFilterContext context)
{
bool hasAuthorizeAttribute = HasAttribute(context.MethodInfo, typeof(AuthorizeAttribute), true);
bool hasAnonymousAttribute = HasAttribute(context.MethodInfo, typeof(AllowAnonymousAttribute), true);
// so far as I understood the action/operation is public/unprotected
// if there is no authorize or an allow anonymous (allow anonymous overrides all authorize)
bool isAuthorized = hasAuthorizeAttribute && !hasAnonymousAttribute;
if (!isAuthorized)
{
operation.Description =
"<p><bold>BIG BOLD LABEL indicating an UPROTECTED PUBLIC method</bold></p>"
+ operation.Description;
}
}
}
and add it with
services.AddSwaggerGen(c => { c.OperationFilter<UnprotectedOperationFilter>();} );
I didn't understand what you mean with filter out different attributes but I hope the code above helps you to check if the attribute is present and do what you desire to do.
I need to call DAO methods outside resource in dropwizard.
Looking at the manual Im unclear how to use it. The manual says
SessionDao dao = new SessionDao(hibernateBundle.getSessionFactory());
ExampleAuthenticator exampleAuthenticator = new
UnitOfWorkAwareProxyFactory(hibernateBundle)
.create(ExampleAuthenticator.class, SessionDao.class, dao);
Can anyone show me the usage of exampleAuthenticator methods which call DAO.
Thanks, Kedar
Working solution
/** initializing proxy dao for authorization */
AuthenticatorDAOProxy authenticatorDAOProxy = new UnitOfWorkAwareProxyFactory(hibernateBundle)
.create(AuthenticatorDAOProxy.class, DeviceDAO.class, deviceDAO);
We can now use authenticatorDAOProxy outside jersey resources
One thing to note., AuthenticatorDAOProxy should have a constructor accepting DeviceDAO
Now your proxyDao will look like
public class AuthenticatorDAOProxy {
private DeviceDAO deviceDAO;
public AuthenticatorDAOProxy(DeviceDAO deviceDAO) {
this.deviceDAO = deviceDAO;
}
#UnitOfWork
public Boolean checkIsDeviceValid(String deviceId, User user) {
Device device = deviceDAO.getByDeviceIdAndUser(deviceId, user);
if (device != null && device.getIsActive() == true) {
return true;
}
return false;
}
}
Each Dropwizard module have a testsuite. Here is the answer you are looking for: https://github.com/dropwizard/dropwizard/blob/release/1.1.x/dropwizard-hibernate/src/test/java/io/dropwizard/hibernate/UnitOfWorkAwareProxyFactoryTest.java#L121-L151
The logic is:
The DAO object instance holds a reference to the Hibernate SessionFactory;
A method that access the DB calls sessionFactory.getCurrentSession(). The sample code is executing a native query and returns true if at least one result row is returned from the DB;
The OAuthAuthenticator instance holds a reference to the DAO instance and calls the appropriate method of the DAO.
The test case is here: https://github.com/dropwizard/dropwizard/blob/release/1.1.x/dropwizard-hibernate/src/test/java/io/dropwizard/hibernate/UnitOfWorkAwareProxyFactoryTest.java#L64-L74
So I've created a custom authorize attribute I use in a few places that is derived from an abstract base class which is derived from AuthorizeAttribute:
CustomAuthorizeAttributeBase.cs
public abstract class CustomAuthorizeAttributeBase : AuthorizeAttribute
{
public abstract string GetUsers();
public abstract string GetRoles();
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
if (filterContext.IsChildAction)
{
return;
}
filterContext.Result =
new RedirectToRouteResult(new RouteValueDictionary
{
{"controller", "NotAuthorized"},
{"action", "Index"},
});
}
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if (GetUsers().IndexOf(httpContext.User.Identity.Name, StringComparison.CurrentCultureIgnoreCase) >= 0 ||
GetRoles().Split(',').Any(s => httpContext.User.IsInRole(s)))
{
return true;
}
return false;
}
}
AreaLevelReadonly.cs
public class AreaLevelReadOnly : CustomAuthorizeAttributeBase
{
public override string GetUsers()
{
return ConfigurationManager.AppSettings["AreaReadonlyUsers"];
}
public override string GetRoles()
{
return ConfigurationManager.AppSettings["AreaReadonlyRoles"];
}
}
I also have some fairly simple code that gets me the currently logged in user:
UserIdentity.cs
public class UserIdentity : IUserIdentity
{
public string GetUserName()
{
return HttpContext.Current.User.Identity.Name.Split('\\')[1];
}
}
However, when I add my AreaLevelReadonly attribute to my controllers, getUserName fails and returns an exception that Name is null. I agonized over it for about an hour before putting authorize attribute on there as well, at which point it magically started working again. So, what is so different on the implementation level that my attribute deriving from authorizeattribute doesn't cause the Name to be populated.
Note: Windows authentication is on for the area, and the code works, but I don't understand why the Readonly attribute isn't enough to trigger authorization and population of the HttpContext.Current.User.Identity.Name.
Edit: Working:
[AreaLevelReadonly]
[Authorize]
public class DeleteAreaDataController : Controller {
//etc
var username = _userIdentity.GetUserName(HttpContext);
//etc
}
Exception on name:
[AreaLevelReadonly]
public class DeleteAreaDataController : Controller {
//etc
var username = _userIdentity.GetUserName(HttpContext);
//etc
}
More likely than not, you're accessing User.Identity.Name before it's populated. By including the standard Authorize attribute, as well, your code is then only running after the user has been authorized already and User.Identity.Name has been populated.
EDIT
Sorry, I misunderstood where the code attempting to call User.Identity.Name was running. Based on the belief that it was happening in your custom attribute, I was suggesting that you're trying to access it too early. However, I now see that you're calling it in your controller (although an explanation of what happens in GetUserAccount(HttpContext) would have helped.)
Anyways, your custom attribute obviously adds extra conditions on whether a user is authorized or not. When you return false, there is no user. It's not a situation where the user is "logged in" but not allowed to see the page. It's either there or it isn't. So the user is failing authorization based on your custom attribute (User.Identity.Name is null) but is authorized when you include Authorize (User.Identity.Name has a value).
Long and short, your GetUserName or GetUserAccount or whatever code needs to account for when the user has failed authorization. Or, if the user shouldn't be failing authorization, you'll need to look into why your custom attribute isn't working. Though, either way, you should still account for User.Identity.Name being null.
Your custom attribute is probably reading User.Identity.Name before you check that the user is authenticated.
In other words, in IsAuthorized(), before you read User.Identity.Name, you should be doing something like this:
if (!user.Identity.IsAuthenticated)
{
// Your custom code...
return false;
}
The reason you need this is because Windows Authentication (at least for NTLM) is a 2-step negotiation process between the client and server (see https://support.citrix.com/article/CTX221693). There will be 2 requests - the first with no name, and the second with a name. You can test this yourself - the source code for AuthorizeAttribute is provided here. Copy/paste that into your code and put a breakpoint in IsAuthorized - you will see that the breakpoint is hit twice. First time, the name is null, second time, it's set to your username.
So I think the solution is to either check user.Identity.IsAuthenticated at the start of your method, if you need to run custom code (as shown above), or alternatively if you only need to return false, simply replace the above code with base.IsAuthorized() which should do it for you.
Is there any way to define the users that can use my application in a list in Config.groovy? This will be using Grails 2.2.3 and the latest versions of Spring Security Core and Spring Security LDAP.
We use Active Directory for authentication, and only 2 or 3 people will use this little application, so it doesn't seem worthy of making an AD Group for just this app. It would be simpler to define a list, and any time there is a new hire instead of adding them to the AD group all I have to do is add their name to the external Grails config.
I would like to do something like the following:
SomeController.groovy
#Secured("authentication.name in grailsApplication.config.my.app.usersList")
class SomeController {
}
Then in Config.groovy put this code:
my.app.usersList = ['Bill', 'Tom', 'Rick']
Is this possible? If so, is this a terrible idea? Thanks a lot.
That seems really silly. Why not have the list of users in a table? Then you can add/remove from that table without have to modify the application.
I currently do this and in my UserDetailsContextMapper I make sure the username already exists in the Users table.
You need a custom authenticator that will try to access your Active Directory and if authenticated, will look into Grails properties to check if the username is allowed to login.
This is the class that I use. I changed the code to validate the config:
class ActiveDirectoryAuthenticator {
private DefaultSpringSecurityContextSource contextFactory
private String principalSuffix = ""
def grailsApplication
public DirContextOperations authenticate(Authentication authentication) {
// Grab the username and password out of the authentication object.
String principal = authentication.getName() + "#" + principalSuffix
String password = ""
if (authentication.getCredentials() != null) {
password = authentication.getCredentials().toString()
}
// If we have a valid username and password, try to authenticate.
if (!("".equals(principal.trim())) && !("".equals(password.trim()))) {
try {
String provider = contextFactory.getUrls()[0]
Hashtable authEnv = new Hashtable(11)
authEnv.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap.LdapCtxFactory")
authEnv.put(Context.PROVIDER_URL, provider)
authEnv.put(Context.SECURITY_AUTHENTICATION, "simple")
authEnv.put(Context.SECURITY_PRINCIPAL, principal)
authEnv.put(Context.SECURITY_CREDENTIALS, password)
javax.naming.directory.DirContext authContext = new InitialDirContext(authEnv)
//here validate the user against your config.
if(!authentication.getName() in grailsApplication.config.adUsersAllowed) {
throw new BadCredentialsException("User not allowed.")
}
DirContextOperations authAdapter = new DirContextAdapter()
authAdapter.addAttributeValue("ldapContext", authContext)
return authAdapter
} catch ( NamingException ex ) {
throw new BadCredentialsException(ex.message)
}
} else {
throw new BadCredentialsException("Incorrect username or password")
}
}
public DefaultSpringSecurityContextSource getContextFactory() {
return contextFactory
}
/**
* Set the context factory to use for generating a new LDAP context.
*
* #param contextFactory
*/
public void setContextFactory(DefaultSpringSecurityContextSource contextFactory) {
this.contextFactory = contextFactory
}
public String getPrincipalSuffix() {
return principalSuffix
}
/**
* Set the string to be prepended to all principal names prior to attempting authentication
* against the LDAP server. (For example, if the Active Directory wants the domain-name-plus
* backslash prepended, use this.)
*
* #param principalPrefix
*/
public void setPrincipalSuffix(String principalSuffix) {
if (principalSuffix != null) {
this.principalSuffix = principalSuffix
} else {
this.principalSuffix = ""
}
}
}
Declare it as your ldapAuthenticator in resources.groovy:
ldapAuthenticator(ActiveDirectoryAuthenticator) {
contextFactory = ref('contextSource')
principalSuffix = 'domain.local' //your domain suffix
grailsApplication = ref('grailsApplication')
}
The downside is that you need to restart your context when you change config.groovy
In your controllers just use #Secured('IS_AUTHENTICATED_FULLY')
I do not think you can do that because annotations are resolved at compile time and not in runtime. Config properties will be read during the application runtime so you I fear you have to end up doing:
#Secured(["authentication.name in ['Bill', 'Tom', 'Rick']"])
class SomeController {
}
If I remember correctly the #Secured annotation cannot be used for other things than comparing roles. But you should be able to do this with spring securities #PreAuthorize and #PostAuthorize annotations. When using grails the easiest way to setup these annotations is installing the spring security ACL plugin.
Within #PreAuthorize and #PostAuthorize you can use SPEL expressions which are more flexible. Unfortunatelly SPEL does not provide an in operator. However you can delegate the security check to a service:
#PreAuthorize('#securityService.canAccess(authentication)')
public void test() {
println "test?"
}
With the # symbol you can reference other beans like services within expression. Here the method securityService.canAccess() is called to evaluate if the logged in user can access this method.
To use this you have to configure a BeanResolver. I wrote some more details about configuring a BeanResolver here.
Within securityService you can now do:
class SecurityService {
def grailsApplication
public boolean canAccess(Authentication auth) {
return grailsApplication.config.myList.contains(auth.name)
}
}
In general I would not recommend to use a configuration value for validating the user in security checks. The groovy configuration will be compiled so you cannot easily add a new user without redeploying your application.