I have the Grails Spring Security plugin connecting to one Active Directory server with no problems. However, I need to connect to multiple servers. We have some users on one AD server and other users on a different server, so we need to try looking for users in both locations.
For example, in Java I have this working as below:
<authentication-manager>
<authentication-provider ref="provider1"/>
<authentication-provider ref="provider2"/>
...
</authentication-manager>
<ldap-server id="provider1"
url="ldap://LDAPSERVER1.mycompany.intranet"
manager-dn="OU=std_users,OU=users,DC=mycompany,DC=intranet"
manager-password="blah"/>
<ldap-server id="provider2"
url="ldap://DIFFERENT_LDAPSERVER.mycompany.intranet"
manager-dn="OU=std_users,OU=external_users,DC=mycompany,DC=intranet"
manager-password="blah"/>
In Grails I can configure one AD server but cannot work out how to configure more than one:
// LDAP config
grails.plugin.springsecurity.ldap.context.managerDn = 'CN=blah,OU=std_users,OU=users,DC=mycompany,DC=intranet'
grails.plugin.springsecurity.ldap.context.managerPassword = 'the_password'
grails.plugin.springsecurity.ldap.context.server = 'ldap://theserver.mycompany.intranet'
grails.plugin.springsecurity.ldap.authorities.ignorePartialResultException = true // typically needed for Active Directory
grails.plugin.springsecurity.ldap.search.base = 'OU=std_users,OU=users,DC=mycompany,DC=intranet'
grails.plugin.springsecurity.ldap.search.filter="sAMAccountName={0}" // for Active Directory you need this
grails.plugin.springsecurity.ldap.search.searchSubtree = true
grails.plugin.springsecurity.ldap.auth.hideUserNotFoundExceptions = false
I know that you can create a space-separated list of servers but this won't work for me as it will only try one of the servers once it has a connection, whereas I need it to try looking for users in both.
I think I probably need to get stuck into the resources.groovy file but don't know where to start with this - has anyone configured multiple AD locations?
The only other idea I have is to create a virtual directory which brings together all the users in one directory. Can anyone suggest a good way of doing this? I have been looking at http://myvd.sourceforge.net/usecases.html
Any help would be appreciated. Have been googling all day and I am no closer to a solution.
Andrew's answer pointed me in the right direction and I now have this working.
It was A LOT easier to make this work using ActiveDirectoryLdapAuthenticationProvider. This is done as below:
In resources.groovy:
// Domain 1
ldapAuthProvider1(ActiveDirectoryLdapAuthenticationProvider,
"mydomain.com",
"ldap://mydomain.com/"
)
// Domain 2
ldapAuthProvider2(ActiveDirectoryLdapAuthenticationProvider,
"mydomain2.com",
"ldap://mydomain2.com/"
)
In Config.groovy:
grails.plugin.springsecurity.providerNames = ['ldapAuthProvider1', 'ldapAuthProvider2']
This is all the code you need. You can pretty much remove all other grails.plugin.springsecurity.ldap.* settings in Config.groovy as they don't apply to this AD setup.
For documentation, see:
http://docs.spring.io/spring-security/site/docs/3.1.x/reference/springsecurity-single.html#ldap-active-directory
If you aren't using AD and want the 'pure LDAP' version:
In resources.groovy:
// Create another ldap authentication provider
ldapAuthProvider2(org.springframework.security.ldap.authentication.LdapAuthenticationProvider,
ref("ldapAuthenticator2"),
ref("ldapAuthoritiesPopulator") // Use default
) {
// Can set other auth provider settings here
}
ldapAuthenticator2(org.springframework.security.ldap.authentication.BindAuthenticator, ref("contextSource2")) {
userSearch = ref("ldapUserSearch2")
}
// Set up the manager to read LDAP
contextSource2(DefaultSpringSecurityContextSource, grailsApplication.config.grails.plugin.springsecurity.ldap.context.server2) {
userDn = grailsApplication.config.grails.plugin.springsecurity.ldap.context.managerDn2 // Manager DN
password = grailsApplication.config.grails.plugin.springsecurity.ldap.context.managerPassword2
}
// Configuration for searching for user
ldapUserSearch2(FilterBasedLdapUserSearch, grailsApplication.config.grails.plugin.springsecurity.ldap.search.base2, grailsApplication.config.grails.plugin.springsecurity.ldap.search.filter2, ref('contextSource2')) {
}
And then in Config.groovy:
// Config for second LDAP AuthenticationProvider - used in resources.groovy
grails.plugin.springsecurity.ldap.context.managerDn2 = 'CN=MANAGER_USER,OU=Users,DC=mycompany,DC=com'
grails.plugin.springsecurity.ldap.context.managerPassword2 = 'manager_password'
grails.plugin.springsecurity.ldap.context.server2 = "ldap://the-ldap-server.com"
grails.plugin.springsecurity.ldap.search.base2 = 'OU=Users,DC=mycompany,DC=com'
grails.plugin.springsecurity.ldap.search.filter2 = "sAMAccountName={0}" // for Active Directory you need this
// Add the AuthenticationProvider to the list
grails.plugin.springsecurity.providerNames = ['ldapAuthProvider', 'ldapAuthProvider2']
This link was very useful for finding out how to set this up:
https://github.com/grails-plugins/grails-spring-security-ldap/blob/master/SpringSecurityLdapGrailsPlugin.groovy
The basic idea would be to construct a second, custom AuthenticationProvider bean within your application's grails-app/conf/spring/resources.groovy file, perhaps modeling it after ldapAuthProvider in https://github.com/grails-plugins/grails-spring-security-ldap/blob/master/SpringSecurityLdapGrailsPlugin.groovy Then, you could add this custom LDAP authenticator bean to the grails.plugin.springsecurity.providerNames list in Config.groovy (or equivalent)
Related
I'm trying to read the log4j2 configuration from config server during application startup.
bootstrap.yml
spring:
application:
name: loggingApp
cloud:
config:
uri: http://localhost:8888
logging:
config: http://localhost:8888/loggingApp/raw/master/loggingApp-log4j2-DEV.xml
The application seems to get the configuration properly during start up as I see the appropriate log levels. However, the automatic configuration doesn't seem to work. When I change the log level of the loggers, looks like it didn't read the updated config from config server after the monitorInterval has passed. I've set the monitorInterval to 10 seconds. As per the documentation the minimum interval should be 5 seconds. If I point to a file on local drive, instead of the config server url, it is working fine. I tried using -Dlog4j.configurationFile as jvm arg as well as spring configuration logging.config to see if one of those work, but none worked.
https://logging.apache.org/log4j/2.x/manual/configuration.html#AutomaticReconfiguration
https://logging.apache.org/log4j/2.x/log4j-spring-cloud-config/log4j-spring-cloud-config-client/index.html
I'm using spring-boot 2.2.5.RELEASE, log4j2 2.13.1 and spring-cloud Hoxton.SR3 versions.
This is how the git repo looks like where config files are being read from
I too tried the same and it did not work. It seems the <Configuration monitorInternal="10"> property would only have worked if we use log4j.configurationFile property to load the log4j2 config file as mentioned in Apache's Log4j Spring Cloud Configuration Guide .
I used the spring actuator refresh approach to accomplish this task. However this does not reload the configs on its own. So there is work around to meet the required ends. Create a scheduler in your spring application that hits the actuator refresh url on specified intervals as below
#Component
#EnableScheduling
public class RefreshScheduler {
#Autowired
private RestTemplate restTemplate;
#Scheduled(fixedRate = 60000)
private void postRefreshEndPoint() {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<String> entity = new HttpEntity<>(headers);
restTemplate.postForEntity("http://localhost:8080/actuator/refresh", entity, String.class);
}
}
This is surely a very dirty way of somehow achieving the required functionality. It works though :)
As a side note : Isn't it better to not have automatic refresh? My argument would be : It's possible developers by mistake ( or just for fun ) do changes to the configs in GIT. And this will lead to a change in the live application in prodcution.
However if we have the actuator refresh URL as mandatory for refreshing the spring context, this can be stopped. Only authorized memebers from the production/operations team would have access to this refresh url and they can decide whether to actually refresh the production spring beans when a commit on GIT has been done.
I have a Grails application that is successfully using the latest spring-security-core:2.0-RC4 and spring-security-ldap:2.0-RC2. Users can login perfectly using
grails.plugin.springsecurity.ldap.search.base setting for LDAP login authentication.
There is a different setting for the rememberMe userDnBase (mapper) and that setting is:
grails.plugin.springsecurity.ldap.rememberMe.usernameMapper.userDnBase
The LDAP authentication grails.plugin.springsecurity.ldap.search.base is set to ou=people,dc=sitcudy,dc=edu. As mentioned above - the logins work fine because there is a property called searchSubtree that I have set to true. Unfortunately, the searchSubtree setting does not hold true and carry through consistently within the 'remember-me' portion of the code (.ldap.rememberMe)*. The remember-me portion of the code uses a map for the base DN, grails.plugin.springsecurity.ldap.rememberMe.usernameMapper.userDnBase
so I put in a string in the config.groovy file (the same as for the authentication piece) to map to the base DN of ou=people,dc=sitcudy,dc=edu.... which gets mapped to the DN for the LDAP user look up upon returning to the application for persistence cookie login.
Here's where my problem comes in, most users are segregated into different DIT's in our LDAP system. For example, some uses are in ou=staff,ou=people,dc=sitcudy,dc=edu while other users are in ou=students,ou=people,dc=sitcudy,dc=edu therefore, because of the remember me mapping, upon returning to the application, once verifying the cookie, the code tries to bind users in this format, uid=reuben_marcus,ou=people,dc=sitcudy,dc=edu which doesn't exist. What does exist is uid=reuben_marcus,ou=staff,ou=people,dc=sitcudy,dc=edu therefore the cookie is destroyed and the login (IS_AUTHENTICATED_REMEMBERED) never occurs.
If I change grails.plugin.springsecurity.ldap.rememberMe.usernameMapper.userDnBase
to ou=staff,ou=people,dc=sitcudy,dc=edu the remember me functionality works perfect for all staff members, but it doesn't work for all other people - students, faculty etc.
The main setting in question below for me in this issue is:
grails.plugin.springsecurity.ldap.rememberMe.usernameMapper.userDnBase
Since this is just a mapping and there isn't allowance for multiple userDNBases or searchSubtree search.. How is the ‘remember-me’ code supposed to find users that do not fall into this base DN setting...??
I wonder if I'm doing something wrong or if this is a feature request to have the ‘remember me’ code have options for multiple mapping userDNBases or allow it to have a searchSubtree search capability.
Relevant settings from my config.groovy:
grails.plugin.springsecurity.ldap.mapper.roleAttributes = 'sitPriRole,uid'
grails.plugin.springsecurity.ldap.context.managerDn = 'uid=SPS_bind,ou=People,dc=sitcudy,dc=edu'
grails.plugin.springsecurity.ldap.context.managerPassword = 'xxx'
grails.plugin.springsecurity.ldap.context.server = 'ldap://ds01.sitcudy.edu:389'
grails.plugin.springsecurity.ldap.authorities.groupSearchBase ='ou=Groups,dc=sitcudy,dc=edu'
grails.plugin.springsecurity.ldap.search.base = 'ou=People,dc=sitcudy,dc=edu'
grails.plugin.springsecurity.ldap.search.searchSubtree = true
grails.plugin.springsecurity.ldap.auth.hideUserNotFoundExceptions = false
grails.plugin.springsecurity.ldap.search.attributesToReturn = ['uid', 'sitPriRole', 'mail', 'displayName']
grails.plugin.springsecurity.providerNames = ['ldapAuthProvider', 'anonymousAuthenticationProvider', 'rememberMeAuthenticationProvider']
grails.plugin.springsecurity.ldap.authorities.retrieveGroupRoles = false
grails.plugin.springsecurity.ldap.authorities.retrieveDatabaseRoles = false
grails.plugin.springsecurity.password.algorithm = 'SHA-256'
grails.plugin.springsecurity.rememberMe.persistent = true
grails.plugin.springsecurity.rememberMe.persistentToken.domainClassName = 'od.PersistentLogin'
// role-specific LDAP config
// grails.plugin.springsecurity.ldap.useRememberMe = true
grails.plugin.springsecurity.ldap.rememberMe.detailsManager.attributesToRetrieve = null
grails.plugin.springsecurity.ldap.rememberMe.detailsManager.groupMemberAttributeName = 'uniquemember'
grails.plugin.springsecurity.ldap.rememberMe.detailsManager.groupRoleAttributeName = 'cn'
grails.plugin.springsecurity.ldap.rememberMe.detailsManager.groupSearchBase = 'ou=Groups,dc=sitcudy,dc=edu'
grails.plugin.springsecurity.ldap.rememberMe.detailsManager.passwordAttributeName = 'userPassword'
grails.plugin.springsecurity.ldap.rememberMe.usernameMapper.userDnBase = 'ou=People,dc=sitcudy,dc=edu'
grails.plugin.springsecurity.ldap.rememberMe.usernameMapper.usernameAttribute = 'uid'
This problem was mentioned here: Grails - Spring security plugin ldap: remember me not working
I found a workaround for this by registering custom TokenBasedRememberMeServices bean in resources.groovy.
I didn't use persistent logins functionality available in grails-spring-security-ldap plugin, because I found it incompatible with my Active Directory tree layout. Most probably, this could be customized by extending LdapUserDetailsManager but in my situation I found it unnecessary to store token in database.
I used regular spring security remember me cookie option but without storing user password in the cookie. I extended the following methods from TokenBasedRememberMeServices
makeTokenSignature - make token signature without password field
processAutoLoginCookie- if cookie exists, then retrieve username from cookie token and fetch ldap user details (I had to write my own method retrieveUserFromLdap() explained later)
onLoginSuccess - this gets triggered when user logs in with remember-me option checked. Here, I'm removing password and saving token signature to cookie.
To fetch user details and roles from LDAP it might depend on specific implementation but my method looks like this:
static protected UserDetails retrieveUserFromLdap(String username) {
def ldapUserSearch = Holders.applicationContext.getBean('ldapUserSearch')
def userContextMapper = Holders.applicationContext.getBean('ldapUserDetailsMapper')
def authoritiesPopulator = Holders.applicationContext.getBean('ldapAuthoritiesPopulator')
def userContext = ldapUserSearch.searchForUser(username)
def userAuthorities = authoritiesPopulator.getGrantedAuthorities(userContext,username)
userContextMapper.mapUserFromContext(userContext,username,userAuthorities)
}
How do you use a JDBCRealm to handle authenticating and authorizing users in servlets? The only example I can find is to create the DataSource in web.xml (such as Authentication against database using shiro 1.2.1).
I do not want to include database credentials in my source tree (for obvious reasons) and would prefer to use a Context defined DataSource via JNDI as I have for every other RDBMS I have used for any other purpose in every other servlet project I have developed.
How do you configure a Shiro JDBCRealm to obtain its DataSource from JNDI?
Vrushank's answer was really close: you don't need to subclass the JdbcRealm here - you can use Shiro's JndiObjectFactory to acquire the DataSource and then reference that DataSource when you configure the JdbcRealm:
[main]
dataSource = org.apache.shiro.jndi.JndiObjectFactory
dataSource.resourceName = java://app/jdbc/myDataSource
jdbcRealm = org.apache.shiro.realm.jdbc.JdbcRealm
jdbcRealm.dataSource = $dataSource
#addt'l config
For a web application, save the file under WEB-INF/shiro.ini.
See Also
https://github.com/danielmt/shiro-primefaces-example/blob/master/src/main/webapp/WEB-INF/shiro.ini
For Shiro to work with permissions with the JDBC realm this parameter is indispensable:
jdbcRealm.permissionsLookupEnabled = true
I wasted many hours on this because the default for this option is false. In other words, if you don't put this option Shiro always return an empty list of permissions.
I commented on #Les Hazlewood answer and on #Recurse comment, but might be that new answer is better option.
In my case I have to use only JDNI datasource name on weblogic and full path on tomcat:
Tomcat:
ds = org.apache.shiro.jndi.JndiObjectFactory
ds.requiredType = javax.sql.DataSource
ds.resourceName = java:/comp/env/oracle/pportal_dev
# JDBC realm config
jdbcRealm = org.apache.shiro.realm.jdbc.JdbcRealm
jdbcRealm.permissionsLookupEnabled = true
jdbcRealm = org.apache.shiro.realm.jdbc.JdbcRealm
jdbcRealm.dataSource = $ds
Weblogic
ds = org.apache.shiro.jndi.JndiObjectFactory
ds.requiredType = javax.sql.DataSource
ds.resourceName = oracle/pportal_dev
# JDBC realm config
jdbcRealm = org.apache.shiro.realm.jdbc.JdbcRealm
jdbcRealm.permissionsLookupEnabled = true
jdbcRealm.dataSource = $ds
Note
ds.resourceName = java:/comp/env/oracle/pportal_dev
vs
ds.resourceName = oracle/pportal_dev
You'll need to create a custom Realm of your own by extending JdbcRealm to programatically lookup the datasource through the provided JNDI.
You can then pass the JNDI as a property in shiro.ini
[main]
# realms to be used
customSecurityRealm=package.to.your.CustomRealm
customSecurityRealm.jndiDataSourceName=java:app/jdbc/myDatasource
See the below article as an example. It takes care of both Authentication and Authorization.
Apache Shiro JDBC Realm
I want to implement permission-based access control as discussed in this post.
I am not familiar with the implementation - is there any detailed example on how to implement this from start to finish?
Have you seen the Spring security plugin? Main docs are here
As in this post you could also consider the Shiro plugin as well. I find the Spring security plugin a simpler but the Shiro one does have some advantages as discussed in the post.
I have just solved this (mostly) so I post it here for reference. This solution works on Grails 2.2.4 and Spring Security 2.0 RC
1) Security Domain Model
You model your security domain classes as described in the article, so you will have these domains in the end:
- Permission
- Role
- RolePermission
- User
- UserRole
2) Querying authorities for users
You make sure that your User class returns Permissions instead of Roles as authorities in the getAuthorities() method:
/**
* Gets authorities for the user.
*
* It will return all of the Permissions of the User assigned by the Roles
* which the User has
*/
Set<Permission> getAuthorities() {
Set allPermissions = []
// Collect all Roles of the User
UserRole.findAllByUser(this).each { userRole ->
// Collect all Permissions from the Role
Role role = userRole.role
// Returning the collected permissions
RolePermission.findAllByRole(role).each { rp ->
allPermissions.add(rp.permission)
}
}
return allPermissions
}
3) Spring Security Config
I have this in my Config.groovy for Spring Security configuration (non-relevant parts omitted):
grails {
plugin {
springsecurity {
...
userLookup {
userDomainClassName = 'limes.security.User'
}
authority {
nameField = 'name'
className = 'limes.security.Permission'
}
...
}
}
}
One important highlight is the authority.nameField which MUST conform with your Permission class. The name attribute is called 'name' in my model (and the article).
Naturally, you set your Permission class as the authority.className in order to make it fit with the return values of User.getAuthorities().
4) Using in security expressions
The solution above does not solve the limitation of the Grails Spring Security plugin that you can use only authority name starting with "ROLE_".
So, if you want to call your permissions like "PERM_PERMISSION1", than you have to write EL expressions for checks everywhere (notybly on controller #Secured annotations and static url rules).
So instead of
#Secured(["PERM_PERMISSION1"])
You write
#Secured(["hasRole('PERM_PERMISSION1')"])
Everytime i call subject.isPermitted(), it sends a sql to db.
How can i cache it? Any example? Thanks.
I read the doc of shiro grails plugin, but cant solove it.
DataSource:
hibernate {
cache.use_second_level_cache = true
cache.use_query_cache = true
cache.provider_class = 'net.sf.ehcache.hibernate.EhCacheProvider'
}
How to set the cachemanager to shiro? I try spring.resource,throw an error.
What's the instance bean name of cachemanager? Do i need to config sth else?
You'll need to configure an org.apache.shiro.cache.CacheManager instance on Shiro's SecurityManager. Most of Shiro's out-of-the-box Realm implementations know how to work with a configured CacheManager and will cache AuthorizationInfo returned from a Realm permission lookup automatically.
I'm not sure how to do this using the Grails Shiro plugin, but in Shiro's INI, you would do that this way:
[main]
...
cacheManager = com.my.implementation.of.CacheManager
securityManager.cacheManager = $cacheManager
...
I'd recommend asking the grails-user mailing list to see if there is a more 'grailsy' way to configure this for the Grails Shiro plugin.
HTH,
Les