Grails spring security rest inject tokenGenerator - grails

I have a similar requirement like this post mentioned. :REST spring security - Manually authenticating a new user and getting access token
According to the accepted answer, the codes will be like:
class RegisterController {
def springSecurityService
def tokenGenerator
def tokenStorageService
def register() {
//do stuff
springSecurityService.reauthenticate(username)
String tokenValue = tokenGenerator.generateToken()
tokenStorageService.storeToken(tokenValue, springSecurityService.principal)
redirect url: "http://example.org/?access_token=${tokenValue}"
}
}
I tried but it didn't work for me. It seems the TokenGenerator implementation class is not injected right. I understand the default implementation in grails-spring-security-rest TokenGenerator will be the JWT but wonder where should I register or config it.

Well if you want to use the "tokenGenerator" then you need to register it under the "resources.groovy" like below
// creating the bean of token generator
tokenGenerator(SecureRandomTokenGenerator)
and then inject it into your controller or service like below
class RegisterController {
def springSecurityService
def tokenGenerator
def tokenStorageService
def register() {
//do stuff
springSecurityService.reauthenticate(username)
String tokenValue = tokenGenerator.generateToken()
tokenStorageService.storeToken(tokenValue, springSecurityService.principal)
redirect url: "http://example.org/?access_token=${tokenValue}"
}
}
I have followed the same example (with slight modification) and its working as expected.
I have used the "userDetailsService" for generating user instance instead of "springSecurityService.reauthenticate(username)"
So my function looks like below.
/**
* For generating the access token for the user
*
* #param userName : Holds the username of the user
*
* #return : access token
*/
String generateAccessToken(String userName){
String tokenValue
try{
//load user details
def userDetails = userDetailsService.loadUserByUsername(userName)
//generate access token
tokenValue = tokenGenerator.generateAccessToken(userDetails).accessToken
//store access token
tokenStorageService.storeToken(tokenValue, userDetails)
} catch (Exception e){
//Exception handling code
}
return tokenValue
}

Related

How to create a login function without using spring security with grails 3 using interceptors

System has two grails apps:
the private backoffice which uses springsecurity and an Operator domain object holding the operators username, password, number of failed logins etc.
the public web front end where users signup, login, and use the system. The User domain object holds the users username, password etc.
Because we are using springsecuirty for the backoffice, I assume we cant use it again for the web (the config and db will conflict). Also, the web just needs a very basic auth (all pages require a valid session except register and the login form itself).
Setting up the login form and the interceptor are easy.
The question is, what should the login form actually do in the controller? I can check the username and password match whats in the DB, then I presumably need to create a session, with session timeouts etc. Where do I look for documentation on how to do this? http://docs.grails.org/3.1.1/ref/Servlet%20API/session.html Tells you how to logout, but not login. I presumably need to store sessions in the DB (so that the user can hit any server) etc.
By looking at some of my old java code, I have got some of the way there.
The interceptor looks like this:
class AuthInterceptor {
public AuthInterceptor() {
// allow the login form and register form to work.
matchAll().excludes(controller: 'auth')
}
boolean before() {
if(session.getAttribute("user")== null ) {
// jump to the login form if there is no user attribute.
redirect controller: 'auth', action: 'login'
return false
}
true
}
boolean after() { true }
void afterView() {
// no-op
}
The controller looks like this:
class AuthController {
def index() { }
def login() {
def email = params.email
def password = params.password
if (email != null) {
// It would be better to invalidate the old session
// but if we do, we cant get a new one...
// session.invalidate()
User user = User.findByEmail(email);
if (user != null) {
log.error("user.pass:" + user.password + " pass:" + password)
// #TODO handle password encryption
if (user.password == password) {
session.setAttribute("user", user)
redirect(controller:"dashboard")
}
}
flash.message = "email or password incorrect"
}
render (view:"login")
} // login()
However, I have not found where we can set the session timeout yet.

No Access To UserDetailsService in LogoutController When Logged into Grails Application via SAML

Using Spring SAML Security, I've enabled our application as a Service Provider. This works great, I use a custom WickedUserDetails (extended from GrailsUser), and everything populates as it should.
Now I'm trying to implement the global logout for SAML, but even before I can do anything "fancy," I've noticed that when I hit our regular LogoutController, I do not have access to WickedUserDetails. I just have an anonymous grails user.
This behavior happens when I try to access /logout/index and /logout/special. It works as expected when I use SlogoutController.
class LogoutController {
def springSecurityService
/**
* Index action. Redirects to the Spring security logout uri.
*/
def index = {
// Populates with ANONYMOUS GRAILS USER when logged in via SAML
// but with WickedUserDetails when logged in via the "normal" Spring Security mechanism
def check = springSecurityService.principal
redirect uri: SpringSecurityUtils.securityConfig.logout.filterProcessesUrl // '/j_spring_security_logout'
}
def special = {
// Populates with ANONYMOUS GRAILS USER when logged in via SAML
// but with WickedUserDetails when logged in via the "normal" Spring Security mechanism
def check = springSecurityService.principal
redirect uri: SpringSecurityUtils.securityConfig.logout.filterProcessesUrl // '/j_spring_security_logout'
}
}
class SlogoutController {
def springSecurityService
def special = {
// Populates with WickedUserDetails
def check = springSecurityService.principal
redirect uri: SpringSecurityUtils.securityConfig.logout.filterProcessesUrl // '/j_spring_security_logout'
}
// Populates with WickedUserDetails
def index = {
def check = springSecurityService.principal
redirect uri: SpringSecurityUtils.securityConfig.logout.filterProcessesUrl // '/j_spring_security_logout'
}
}
In my Config.groovy, I don't have any special interceptor URLs set up, the only reference I have to the logout URL is:
grails.plugin.springsecurity.secureChannel.definition = [
'/login/**' : 'REQUIRES_SECURE_CHANNEL',
'/logout/**' : 'REQUIRES_INSECURE_CHANNEL'
]
This is the only reference I have set up in UrlMappings:
"/logout/$action?"(controller: "logout")
Can someone please explain to me why this behavior happens? I can come up with a workaround in my app, but I am insanely curious as to what's going on.
OK, the issue was my taking shortcuts. In the authentication provider originally, I was not updating the bespoke token with the SAML credentials
Authentication authenticate(Authentication authentication) {
if (authentication instanceof SAMLAuthenticationToken) {
def samlToken = super.authenticate(authentication)
if (samlToken) {
String username = samlToken.principal
LightweightContact contact = new LightweightContact(username: username)
contact = contact.findByUsername()
boolean isContactValid = contactValid(contact, samlToken)
if (isContactValid) {
WickedAuthenticationToken wickedToken = MyNormalSpringCustomWickedAuthenticationProvider.getWickedAuthenticationToken(contact)
// -------- I needed to explicitly set the credentials here -----------------------
wickedToken.setCredentials(samlToken.credentials)
return wickedToken
}
}
}
// the authentication token was not of the correct class
// or no user was found for it
return null
}

How to propagate spring security context to JMS?

I have a web application which sets a spring security context through a spring filter. Services are protected with spring annotations based on users roles. This works.
Asynchronous tasks are executed in JMS listeners (extend javax.jms.MessageListener). The setup of this listeners is done with Spring.
Messages are sent from the web application, at this time a user is authenticated. I need the same authentication in the JMS thread (user and roles) during message processing.
Today this is done by putting the spring authentication in the JMS ObjectMessage:
SecurityContext context = SecurityContextHolder.getContext();
Authentication auth = context.getAuthentication();
... put the auth object in jms message object
Then inside the JMS listener the authentication object is extracted and set in the context:
SecurityContext context = new SecurityContextImpl();
context.setAuthentication(auth);
SecurityContextHolder.setContext(context);
This works most of the time. But when there is a delay before the processing of a message, message will never be processed. I couldn't determine yet the cause of these messages loss, but I'm not sure the way we propagate authentication is good, even if it works in custer when the message is processed in another server.
Is this the right way to propagate a spring authentication ?
Regards,
Mickaƫl
I did not find better solution, but this one works for me just fine.
By sending of JMS Message I'am storing Authentication as Header and respectively by receiving recreating Security Context. In order to store Authentication as Header you have to serialise it as Base64:
class AuthenticationSerializer {
static String serialize(Authentication authentication) {
byte[] bytes = SerializationUtils.serialize(authentication);
return DatatypeConverter.printBase64Binary(bytes);
}
static Authentication deserialize(String authentication) {
byte[] decoded = DatatypeConverter.parseBase64Binary(authentication);
Authentication auth = (Authentication) SerializationUtils.deserialize(decoded);
return auth;
}
}
By sending just set Message header - you can create Decorator for Message Template, so that it will happen automatically. In you decorator just call such method:
private void attachAuthenticationContext(Message message){
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
String serialized = AuthenticationSerializer.serialize(auth);
message.setStringProperty("authcontext", serialized);
}
Receiving gets more complicated, but it can be also done automatically. Instead of applying #EnableJMS use following Configuration:
#Configuration
class JmsBootstrapConfiguration {
#Bean(name = JmsListenerConfigUtils.JMS_LISTENER_ANNOTATION_PROCESSOR_BEAN_NAME)
#Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public JmsListenerAnnotationBeanPostProcessor jmsListenerAnnotationProcessor() {
return new JmsListenerPostProcessor();
}
#Bean(name = JmsListenerConfigUtils.JMS_LISTENER_ENDPOINT_REGISTRY_BEAN_NAME)
public JmsListenerEndpointRegistry defaultJmsListenerEndpointRegistry() {
return new JmsListenerEndpointRegistry();
}
}
class JmsListenerPostProcessor extends JmsListenerAnnotationBeanPostProcessor {
#Override
protected MethodJmsListenerEndpoint createMethodJmsListenerEndpoint() {
return new ListenerEndpoint();
}
}
class ListenerEndpoint extends MethodJmsListenerEndpoint {
#Override
protected MessagingMessageListenerAdapter createMessageListenerInstance() {
return new ListenerAdapter();
}
}
class ListenerAdapter extends MessagingMessageListenerAdapter {
#Override
public void onMessage(Message jmsMessage, Session session) throws JMSException {
propagateSecurityContext(jmsMessage);
super.onMessage(jmsMessage, session);
}
private void propagateSecurityContext(Message jmsMessage) throws JMSException {
String authStr = jmsMessage.getStringProperty("authcontext");
Authentication auth = AuthenticationSerializer.deserialize(authStr);
SecurityContextHolder.getContext().setAuthentication(auth);
}
}
I have implemented for myself a different solution, which seems easier for me.
Already I have a message converter, the standard JSON Jackson message converter, which I need to configure on the JMSTemplate and the listeners.
So I created a MessageConverter implementation which wraps around another message converter, and propagates the security context via the JMS message properties.
(In my case, the propagated context is a JWT token which I can extract from the current context and apply to the security context of the listening thread).
This way the entire responsibility for propagation of security context is elegantly implemented in a single class, and requires only a little bit of configuration.
Thanks great but I am handling this in easy way . put one util file and solved .
public class AuthenticationSerializerUtil {
public static final String AUTH_CONTEXT = "authContext";
public static String serialize(Authentication authentication) {
byte[] bytes = SerializationUtils.serialize(authentication);
return DatatypeConverter.printBase64Binary(bytes);
}
public static Authentication deserialize(String authentication) {
byte[] decoded = DatatypeConverter.parseBase64Binary(authentication);
Authentication auth = (Authentication) SerializationUtils.deserialize(decoded);
return auth;
}
/**
* taking message and return string json from message & set current context
* #param message
* #return
*/
public static String jsonAndSetContext(Message message){
LongString authContext = (LongString)message.getMessageProperties().getHeaders().get(AUTH_CONTEXT);
Authentication auth = deserialize(authContext.toString());
SecurityContextHolder.getContext().setAuthentication(auth);
byte json[] = message.getBody();
return new String(json);
}
}

Keep users in Config.groovy list in Grails

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.

grailsApplication null in Service

I have a Service in my Grails application. However I need to reach the config for some configuration in my application. But when I am trying to use def grailsApplication in my Service it still gets null.
My service is under "Services".
class RelationService {
def grailsApplication
private String XML_DATE_FORMAT = "yyyy-MM-dd"
private String token = 'hej123'
private String tokenName
String WebserviceHost = 'xxx'
def getRequest(end_url) {
// Set token and tokenName and call communicationsUtil
setToken();
ComObject cu = new ComObject(tokenName)
// Set string and get the xml data
String url_string = "http://" + WebserviceHost + end_url
URL url = new URL(url_string)
def xml = cu.performGet(url, token)
return xml
}
private def setToken() {
tokenName = grailsApplication.config.authentication.header.name.toString()
try {
token = RequestUtil.getCookie(grailsApplication.config.authentication.cookie.token).toString()
}
catch (NoClassDefFoundError e) {
println "Could not set token, runs on default instead.. " + e.getMessage()
}
if(grailsApplication.config.webservice_host[GrailsUtil.environment].toString() != '[:]')
WebserviceHost = grailsApplication.config.webservice_host[GrailsUtil.environment].toString()
}
}
I have looked on Inject grails application configuration into service but it doesn't give me an answer as everything seems correct.
However I call my Service like this: def xml = new RelationService().getRequest(url)
EDIT:
Forgot to type my error, which is: Cannot get property 'config' on null object
Your service is correct but the way you are calling it is not:
def xml = new RelationService().getRequest(url)
Because you are instantiating a new object "manually "you are actually bypassing the injection made by Spring and so the "grailsApplication" object is null.
What you need to do is injecting your service using Spring like this:
class MyController{
def relationService
def home(){
def xml = relationService.getRequest(...)
}
}

Resources