Spring Security Rest - spring-security

I'm having a set of Sping Data Repositories which are all exposed over Rest by using Spring-data-rest project. Now I want to secure the HTTP, so that only registered users can access the http://localhost:8080/rest/ So for this purpose I add #Secured(value = { "ROLE_ADMIN" }) to all the repositories and I also enable the security by specifying the
#EnableGlobalMethodSecurity(securedEnabled = true, jsr250Enabled = true, prePostEnabled = true)
So now what happens is I go to the rest and it's all good - i'm asked to authenticate. Next thing I do is I go to my website (which uses all the repositories to access the database) but my request fails with
nested exception is org.springframework.security.authentication.AuthenticationCredentialsNotFoundException: An Authentication object was not found in the SecurityContext
which is correct because i'm browsing my website as anonymous user.
So my question is: is there a way to provide method authentication for the REST layer only? To me it sounds like a new annotation is needed (something like #EnableRestGlobalMethodSecurity or #EnableRestSecurity)

I don't know if this will solve your problem, however I managed to get something similar, working for me by creating an event handler for my specific repository, and then used the #PreAuthorize annotation to check for permissions, say on beforeCreate. For example:
#RepositoryEventHandler(Account.class)
public class AccountEventHandler {
private final Logger logger = LoggerFactory.getLogger(getClass());
#PreAuthorize("isAuthenticated() and (hasRole('ROLE_USER'))")
#HandleBeforeCreate
public void beforeAccountCreate(Account account) {
logger.debug(String.format("In before create for account '%s'", account.getName()));
}
#PreAuthorize("isAuthenticated() and (hasRole('ROLE_ADMIN'))")
#HandleBeforeSave
public void beforeAccountUpdate(Account account) {
logger.debug(String.format("In before update for account '%s'", account.getName()));
//Don't need to add anything to this method, the #PreAuthorize does the job.
}
}

Related

How to add parameters to redirect_uri in WebApi Oauth Owin authentication process?

I'm creating a webapi project with oauth bearer token authenthication and external login providers (google, twitter, facebook etc.). I started with the basic VS 2013 template and got everything to work fine!
However, after a user successfully logs is, the owin infrastructure creates a redirect with the folllowing structure:
http://some.url/#access_token=<the access token>&token_type=bearer&expires_in=1209600
In my server code I want to add an additional parameter to this redirect because in the registration process of my app, a new user needs to first confirm and accept the usage license before he/she is registered as a user. Therefore I want to add the parameter "requiresConfirmation=true" to the redirect. However, I've no clue about how to do this. I tried setting AuthenticationResponseChallenge.Properties.RedirectUri of the AuthenticationManager but this doesn't seem to have any affect.
Any suggestions would be greatly appreciated!
It should be relatively easy with the AuthorizationEndpointResponse notification:
In your custom OAuthAuthorizationServerProvider implementation, simply override AuthorizationEndpointResponse to extract your extra parameter from the ambient response grant, which is created when you call IOwinContext.Authentication.SignIn(properties, identity).
You can then add a custom requiresConfirmation parameter to AdditionalResponseParameters: it will be automatically added to the callback URL (i.e in the fragment when using the implicit flow):
public override Task AuthorizationEndpointResponse(OAuthAuthorizationEndpointResponseContext context) {
var requiresConfirmation = bool.Parse(context.OwinContext.Authentication.AuthenticationResponseGrant.Properties.Dictionary["requiresConfirmation"]);
if (requiresConfirmation) {
context.AdditionalResponseParameters.Add("requiresConfirmation", true);
}
return Task.FromResult<object>(null);
}
In your code calling SignIn, determine whether the user is registered or not and add requiresConfirmation to the AuthenticationProperties container:
var properties = new AuthenticationProperties();
properties.Dictionary.Add("requiresConfirmation", "true"/"false");
context.Authentication.SignIn(properties, identity);
Feel free to ping me if you need more details.

How do I automatically pick the configured SAML Identity provider in a multi-tenant environment to do SSO using Spring SAML

I am using Spring SAML in a multi-tenant application to provide SSO. Different tenants use different urls to access the application, and each has a separate Identity Provider configured. How do I automatically assign the correct Identity Provider given the url used to access the application?
Example:
Tenant 1: http://tenant1.myapp.com
Tenant 2: http://tenant2.myapp.com
I saw that I can add a parameter idp to the url (http://tenant1.myapp.com?idp=my.idp.entityid.com) and the SAMLContextProvider will pick the identity provider with that entity id. I developed a database-backed MetadataProvider that takes the tenant hostname as initialisation parameter to fetch the metadata for that tenant form the database linked to that hostname. Now I think I need some way to iterate over the metadata providers to link entityId of the metadata to the hostname. I don't see how I can fetch the entityId of the metadata, though. That would solve my problem.
You can see how to parse available entityIDs out of a MetadataProvider in method MetadataManager#parseProvider. Note that generally each provider can supply multiple IDP and SP definitions, not just one.
Alternatively, you could further extend the ExtendedMetadataDelegate with your own class, include whatever additional metadata (like entityId) you wish, and then simply retype MetadataProvider to your customized class and get information from there when iterating data through the MetadataManager.
If I were you, I'd take a little bit different approach though. I would extend SAMLContextProviderImpl, override method populatePeerEntityId and perform all the matching of hostname/IDP there. See the original method for details.
At the time of writing, Spring SAML is at version 1.0.1.FINAL. It does not support multi-tenancy cleanly out of the box. I found another way to achieve multi-tenancy apart from the suggestions given by Vladimir above. It's very simple and straight-forward and does not require extension of any Spring SAML classes. Furthermore, it utilizes Spring SAML's in-built handling of aliases in CachingMetadataManager.
In your controller, capture the tenant name from the request and create an ExtendedMetadata object using the tenant name as the alias. Next create an ExtendedMetadataDelegate out of the ExtendedMetadata and initialize it. Parse the entity ids out of it and check if they exist in MetadataManager. If they don't exist, add the provider and refresh metadata. Then get the entity id from MetadataManager using getEntityIdForAlias().
Here is the code for the controller. There are comments inline explaining some caveats:
#Controller
public class SAMLController {
#Autowired
MetadataManager metadataManager;
#Autowired
ParserPool parserPool;
#RequestMapping(value = "/login.do", method = RequestMethod.GET)
public ModelAndView login(HttpServletRequest request, HttpServletResponse response, #RequestParam String tenantName)
throws MetadataProviderException, ServletException, IOException{
//load metadata url using tenant name
String tenantMetadataURL = loadTenantMetadataURL(tenantName);
//Deprecated constructor, needs to change
HTTPMetadataProvider httpMetadataProvider = new HTTPMetadataProvider(tenantMetadataURL, 15000);
httpMetadataProvider.setParserPool(parserPool);
//Create extended metadata using tenant name as the alias
ExtendedMetadata metadata = new ExtendedMetadata();
metadata.setLocal(true);
metadata.setAlias(tenantName);
//Create metadata provider and initialize it
ExtendedMetadataDelegate metadataDelegate = new ExtendedMetadataDelegate(httpMetadataProvider, metadata);
metadataDelegate.initialize();
//getEntityIdForAlias() in MetadataManager must only be called after the metadata provider
//is added and the metadata is refreshed. Otherwise, the alias will be mapped to a null
//value. The following code is a roundabout way to figure out whether the provider has already
//been added or not.
//The method parseProvider() has protected scope in MetadataManager so it was copied here
Set<String> newEntityIds = parseProvider(metadataDelegate);
Set<String> existingEntityIds = metadataManager.getIDPEntityNames();
//If one or more IDP entity ids do not exist in metadata manager, assume it's a new provider.
//If we always add a provider without this check, the initialize methods in refreshMetadata()
//ignore the provider in case of a duplicate but the duplicate still gets added to the list
//of providers because of the call to the superclass method addMetadataProvider(). Might be a bug.
if(!existingEntityIds.containsAll(newEntityIds)) {
metadataManager.addMetadataProvider(metadataDelegate);
metadataManager.refreshMetadata();
}
String entityId = metadataManager.getEntityIdForAlias(tenantName);
return new ModelAndView("redirect:/saml/login?idp=" + URLEncoder.encode(entityId, "UTF-8"));
}
private Set<String> parseProvider(MetadataProvider provider) throws MetadataProviderException {
Set<String> result = new HashSet<String>();
XMLObject object = provider.getMetadata();
if (object instanceof EntityDescriptor) {
addDescriptor(result, (EntityDescriptor) object);
} else if (object instanceof EntitiesDescriptor) {
addDescriptors(result, (EntitiesDescriptor) object);
}
return result;
}
private void addDescriptors(Set<String> result, EntitiesDescriptor descriptors) throws MetadataProviderException {
if (descriptors.getEntitiesDescriptors() != null) {
for (EntitiesDescriptor descriptor : descriptors.getEntitiesDescriptors()) {
addDescriptors(result, descriptor);
}
}
if (descriptors.getEntityDescriptors() != null) {
for (EntityDescriptor descriptor : descriptors.getEntityDescriptors()) {
addDescriptor(result, descriptor);
}
}
}
private void addDescriptor(Set<String> result, EntityDescriptor descriptor) throws MetadataProviderException {
String entityID = descriptor.getEntityID();
result.add(entityID);
}
}
I believe this directly solves the OP's problem of figuring out how to get the IDP for a given tenant. But this will work only for IDPs with a single entity id.

Grails pre-authenticated scenario

I'm trying to configure a Grails app in a pre-authenticated scenario, using Spring Security Core.
So I wrote a custom authentication filter:
class MyAuthenticationFilter extends AbstractPreAuthenticatedProcessingFilter {
protected getPreAuthenticatedPrincipal(request) { "my_username" }
protected getPreAuthenticatedCredentials(request) { "N/A" }
}
added it to Spring:
beans = {
myAuthenticationFilter(MyAuthenticationFilter) {
authenticationManager = ref('authenticationManager')
checkForPrincipalChanges = true
}
}
and registered it in BootStrap, with position PRE_AUTH_FILTER:
class BootStrap {
def init = { servletContext ->
SpringSecurityUtils.clientRegisterFilter('myAuthenticationFilter',
SecurityFilterPosition.PRE_AUTH_FILTER.order)
}
}
Config.groovy only has the standard User, Role, and UserRole class names, plus some staticRules.
Inside the filter, I tried returning all sorts of things from its getPreAuthenticatedPrincipal() method: the user id, the user id as a string, its username, the User object itself… I can see that my filter is being called for each request (which is what I want, having set checkForPrincipalChanges = true) no matter what I return from it, the current user remains anonymous: springSecurityService.principal is still __grails.anonymous.user__
What do I need to change in my setup, to be able to authenticate my users, with their existing groups and roles? I don't want to write an additional authentication provider, I'm fine with Grails's standard daoAuthenticationProvider. Do I need to return something specific from my filter? Do I need to setup some other Spring classes?
I solved it. I'm documenting it here in case somebody else needs to do the same.
Basically the missing piece was to configure a new PreAuthenticatedAuthenticationProvider as the only provider. No additional code needed, just define it in resources:
myAuthenticationProvider(PreAuthenticatedAuthenticationProvider) {
preAuthenticatedUserDetailsService = ref('authenticationUserDetailsService')
}
and set it as the only provider in Config:
grails.plugin.pringsecurity.providerNames = ['myAuthenticationProvider']
Then, from the authentication filter, just return the username of the pre-authenticated user, or null if none (the anonymous user.)

Integrating SignalR with existing Authorization

I've been working on a way of integrating SignalR Authorization Attributes with a custom authorization provider (called MVCAuthorization) I went down a few rabbit holes of trying to recreate an Authorization provider for hubs specifically, but that turned out to be far too complicated. So I was wondering, how I can integrate my existing Controller and Action Authorization with my SignalR Hubs and methods?
I figured out that you can retrieve an IAuthorization provider.
If you treat you hub as a controller, and your methods as your actions, all you have to do is create a SignalR Attribute that implements IAuthorizeHubConnection and IAuthorizeHubMethodInvocation
public class HubAuthorizeAttribute : Attribute, IAuthorizeHubConnection,IAuthorizeHubMethodInvocation
{
public virtual bool AuthorizeHubConnection(HubDescriptor hubDescriptor, Microsoft.AspNet.SignalR.IRequest request)
{
IAuthorizationProvider authorizationProvider = DependencyResolver.Current.GetService<IAuthorizationProvider>();
return authorizationProvider.IsAuthorizedController(hubDescriptor.Name);
}
public virtual bool AuthorizeHubMethodInvocation(IHubIncomingInvokerContext hubIncomingInvokerContext)
{
IAuthorizationProvider authorizationProvider = DependencyResolver.Current.GetService<IAuthorizationProvider>();
return authorizationProvider.IsAuthorizedAction(hubIncomingInvokerContext.MethodDescriptor.Hub.Name, hubIncomingInvokerContext.MethodDescriptor.Name);
}
}
Then all you have to do is put the attribute on your hub or any methods you want authorized
[HubAuthorize]
public class Message : Hub
{
public void Send(string message)
{
}
}
You should override the existing methods in the pipeline
Check authorize in SignalR attribute
http://www.asp.net/signalr/overview/signalr-20/security/hub-authorization
Overriding AuthorizeHubMethodInvocation will allow you to authorize the request while overriding UserAuthorized with allow you to authenticate (you can check the user's roles etc.
Have your HubAuthorizeAttribute inherit from AuthorizeAttribute and allow the constructor to take in a list of roles
Here's a simple example on how to handle roles http://www.jasonwatmore.com/post/2014/02/18/ASPNET-Web-API-2-Enum-Authorize-Attribute.aspx

RIA Services: How can I create custom authentication?

I am working with the Silverlight RIA Services and I want to create custom authentication. This appears to be the only thing that has virtually no documentation (I've read through the entire RIAServicesOverview.docx).
Do you know of a way for me to create a customer authentication service? I don't want to use the default ASP.NET membership model. I don't know what interface or abstract class I need to implement - although I did find System.Web.Ria.ApplicationServices.IAuthentication.
Do I need to implement IAuthentication? If so, could you give me some advice on how to go about doing so? These are the following methods:
public User GetUser();
public User Login(string userName, string password, bool isPersistent, string customData);
public User Logout();
public void UpdateUser(User user);
I don't know how I would implement any of these (except for Login) - how could the service possibly know what user is currently logged in in order for Logout() to work?
I've been scouring the web in search of how to do this for hours, and I can't find anything that describes how to create a simple DomainService that can be used for authenticating a user in an "RIA-linked" Silverlight project.
If someone could shed some light on this, I'd be sincerely grateful.
Thanks,
Charles
[EDIT]
I found the RIA Services page on the MSDN Code Gallery. There's a section called Authentication Samples, which links to some great code samples. Check it out if you want to know more about how authentication works within RIA Services.
If you create a "Silverlight Business Application" you'll see how the template implements authentication. (Or just go here and download the template sample project.)
To simplify, here's the process I used:
First, I create a domain service (FooService) that derives from LinqToEntitiesDomainService where FooContext is my entity model. In it I add all the CRUD operations to access my custom DB table and return user profiles.
Next, create a concrete User class on the serverside by deriving from UserBase:
using System.Web.Ria;
using System.Web.Ria.ApplicationServices;
public class User : UserBase
{}
Finally, derive a class from AuthenticationBase and implement the following four methods:
[EnableClientAccess]
public class AuthenticationService : AuthenticationBase<User>
{
private FooService _service = new FooService();
protected override bool ValidateUser(string username, string password)
{
// Code here that tests only if the password is valid for the given
// username using your custom DB calls via the domain service you
// implemented above
}
protected override User GetAuthenticatedUser(IPrincipal pricipal)
{
// principal.Identity.Name will be the username for the user
// you're trying to authenticate. Here's one way to implement
// this:
User user = null;
if (this._service.DoesUserExist(principal.Identity.Name)) // DoesUserExist() is a call
// added in my domain service
{
// UserProfile is an entity in my DB
UserProfile profile = this._service.GetUserProfile(principal.Identity.Name);
user.Name = profile.UserName;
user.AuthenticationType = principal.Identity.AuthenticationType;
}
return user;
}
public override void Initialize(DomainServiceContext context)
{
this._service.Initialize(context);
base.Initialize(context);
}
protected override void Dispose(bool disposing)
{
if (disposing)
this._service.Dispose();
base.Dispose(disposing);
}
}
Here is a complete official example from MS:
http://code.msdn.microsoft.com/Custom-Authentication-96ca3d20
How about implementing the IAuthorization interface?

Resources