Coding our own security extension

While all the features shown in this chapter so far demonstrate a powerful security framework, arguably the most important feature is the ability to write a custom security extension that plugs into this architecture.

Earlier in the LDAP section, it was mentioned that it is possible to adjust where searches are conducted to find groups related to the users. In the SQL section, it was shown that custom queries can be injected. Both of these features allow flexible adjustments, however, they can only bend so far. If our security system is highly specialized, it may be easier to simply code our own extension, and plug it in.

This is also necessary if another security system is not yet supported by Spring Python, such as two-factor tokens, OpenID, or X.509 certificates. While support for these may appear in the future, our needs may not be able to wait.

Coding a custom authentication provider

For our example, let's write a custom authentication provider that is based on logging in to a database instead of doing a password comparison.

Note

This example matches a real situation I had to deal with. I inherited an application that was based on logging the user into the database and then looking up roles. This confined passwords to whatever the database supported, and in turn not allowing us to have a unified policy for acceptable passwords. The first step to migrate that application off of this limited solution required a custom authentication provider just like this example. It allowed me to utilize this framework, which provided the ability to plug in another security provider pointed at our new user security data.

In order to code our custom security provider, we need to write an authenticate method with the following signature:

def authenticate(self, authentication)

Authentication providers are expected to either succeed or fail with the following behavior:

  • If authentication is successful, return an authentication object. It is recommended to create a new instance of UsernamePasswordAuthenticationToken, copy in the username, password, and granted_auths, and then return it. This is the same class used by all of Spring Python Security's providers. We do not have to call setAuthenticated(true) on the token. AuthenticationManager handles this for us
  • If authentication fails, raise a BadCredentialsException

For this provider, we can use DatabaseTemplate to do most of our work. To do that, we need an injected SQL connection factory. If we set the username and password with the values supplied by our user, we can create a DatabaseTemplate and attempt a query to retrieve the user's roles. Since our instance of DatabaseTemplate will try to login to the database before doing the query, this should serve as our authentication check. If successful, we will receive the granted authorities in a result set. If it fails, we will get a SQL exception, which we can catch and replace with a BadCredentialsException.

from springpython.security.providers import *
from springpython.database.core import *
class MySqlLoginAuthenticationProvider(AuthenticationProvider):
def __init__(self, factory=None):
self.factory = factory
def authenticate(self, authentication):
self.factory.username = authentication.username
self.factory.password = authentication.password
dt = DatabaseTemplate(self.factory)
try:
authorities = dt.query("select authority from user where username = ?",
(authentication.username,),
rowhandler=DictionaryRowMapper())
return UsernamePasswordAuthenticationToken(
authentication.username,
authentication.getCredentials(),
[auth["authority"] for auth in authorities])
except:
raise BadCredentialsException("Invalid credentials")

Because each connection factory is slightly different, we will assume this is a MySqlConnectionFactory being passed in. Our new provider is ready for immediate use. It's that easy, meeting our requirement: 'users must be able to quickly write custom security extensions to handle legacy security solutions'.

One way to make this example more sophisticated would be to make it handle the current set of connection factories. But for now, it works as a suitable demonstration of how easy it is to authenticate against an existing system in a way not currently covered by Spring Python Security.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.15.18.198