source: branches/TaskRewrite/src/plugins/acegi-0.5.1/AcegiGrailsPlugin.groovy @ 58

Last change on this file since 58 was 58, checked in by gav, 15 years ago

Configure BootStrap? with latest concepts.
Install and setup Acegi plugin with custom views.
Test Fixture plugin in a test app but couldn't get it to work with Acegi encodePassword() so gave up.

File size: 33.3 KB
Line 
1import grails.util.GrailsUtil
2
3import org.codehaus.groovy.grails.commons.ControllerArtefactHandler
4
5import org.codehaus.groovy.grails.plugins.springsecurity.AnnotationFilterInvocationDefinition
6import org.codehaus.groovy.grails.plugins.springsecurity.AuthenticatedVetoableDecisionManager
7import org.codehaus.groovy.grails.plugins.springsecurity.AuthorizeTools
8import org.codehaus.groovy.grails.plugins.springsecurity.GrailsAccessDeniedHandlerImpl
9import org.codehaus.groovy.grails.plugins.springsecurity.GrailsAuthenticationProcessingFilter
10import org.codehaus.groovy.grails.plugins.springsecurity.GrailsDaoAuthenticationProvider
11import org.codehaus.groovy.grails.plugins.springsecurity.GrailsDaoImpl
12import org.codehaus.groovy.grails.plugins.springsecurity.IpAddressFilter
13import org.codehaus.groovy.grails.plugins.springsecurity.LogoutFilterFactoryBean
14import org.codehaus.groovy.grails.plugins.springsecurity.QuietMethodSecurityInterceptor
15import org.codehaus.groovy.grails.plugins.springsecurity.RequestmapFilterInvocationDefinition
16import org.codehaus.groovy.grails.plugins.springsecurity.Secured as SecuredController
17import org.codehaus.groovy.grails.plugins.springsecurity.SecurityAnnotationAttributes
18import org.codehaus.groovy.grails.plugins.springsecurity.SecurityEventListener
19import org.codehaus.groovy.grails.plugins.springsecurity.WithAjaxAuthenticationProcessingFilterEntryPoint
20
21import org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator
22import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator
23import org.springframework.beans.factory.config.RuntimeBeanReference
24import org.springframework.cache.ehcache.EhCacheFactoryBean
25import org.springframework.cache.ehcache.EhCacheManagerFactoryBean
26import org.springframework.security.annotation.Secured as SecuredService
27import org.springframework.security.context.HttpSessionContextIntegrationFilter
28import org.springframework.security.context.SecurityContextHolder as SCH
29import org.springframework.security.event.authentication.LoggerListener
30import org.springframework.security.intercept.method.MethodDefinitionAttributes
31import org.springframework.security.intercept.web.FilterSecurityInterceptor
32import org.springframework.security.ui.ExceptionTranslationFilter
33import org.springframework.security.ui.basicauth.BasicProcessingFilter
34import org.springframework.security.ui.basicauth.BasicProcessingFilterEntryPoint
35import org.springframework.security.ui.logout.LogoutHandler
36import org.springframework.security.ui.logout.SecurityContextLogoutHandler
37import org.springframework.security.ui.rememberme.NullRememberMeServices
38import org.springframework.security.ui.rememberme.RememberMeProcessingFilter
39import org.springframework.security.ui.rememberme.TokenBasedRememberMeServices
40import org.springframework.security.ui.session.HttpSessionEventPublisher
41import org.springframework.security.ui.switchuser.SwitchUserProcessingFilter
42import org.springframework.security.ui.webapp.AuthenticationProcessingFilter
43import org.springframework.security.util.AntUrlPathMatcher
44import org.springframework.security.util.PortMapperImpl
45import org.springframework.security.util.PortResolverImpl
46import org.springframework.security.util.RegexUrlPathMatcher
47import org.springframework.security.providers.ProviderManager
48import org.springframework.security.providers.anonymous.AnonymousAuthenticationProvider
49import org.springframework.security.providers.anonymous.AnonymousProcessingFilter
50import org.springframework.security.providers.dao.cache.EhCacheBasedUserCache
51import org.springframework.security.providers.dao.cache.NullUserCache
52import org.springframework.security.providers.encoding.MessageDigestPasswordEncoder
53import org.springframework.security.providers.rememberme.RememberMeAuthenticationProvider
54import org.springframework.security.securechannel.ChannelDecisionManagerImpl
55import org.springframework.security.securechannel.ChannelProcessingFilter
56import org.springframework.security.securechannel.InsecureChannelProcessor
57import org.springframework.security.securechannel.RetryWithHttpEntryPoint
58import org.springframework.security.securechannel.RetryWithHttpsEntryPoint
59import org.springframework.security.securechannel.SecureChannelProcessor
60import org.springframework.security.util.FilterChainProxy
61import org.springframework.security.util.FilterToBeanProxy
62import org.springframework.security.vote.AuthenticatedVoter
63import org.springframework.security.vote.RoleVoter
64import org.springframework.security.wrapper.SecurityContextHolderAwareRequestFilter
65import org.springframework.web.filter.DelegatingFilterProxy
66
67/**
68 * Grails Spring Security 2.0 Plugin.
69 *
70 * @author T.Yamamoto
71 * @author Haotian Sun
72 * @author <a href='mailto:beckwithb@studentsonly.com'>Burt Beckwith</a>
73 */
74class AcegiGrailsPlugin {
75
76        private static final String DEFINITION_SOURCE_PREFIX =
77                'CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON\n' +
78                'PATTERN_TYPE_APACHE_ANT\n'
79
80        String version = '0.5.1'
81        String author = 'Tsuyoshi Yamamoto'
82        String authorEmail = 'tyama@xmldo.jp'
83        String title = 'Grails Spring Security 2.0 Plugin'
84        String description = 'Plugin to use Grails domain class and secure your applications with Spring Security filters.'
85        String documentation = 'http://grails.org/AcegiSecurity+Plugin'
86        List observe = ['controllers']
87        List loadAfter = ['controllers']
88        List watchedResources = [
89                'file:./grails-app/controllers/**/*Controller.groovy',
90                'file:./plugins/*/grails-app/controllers/**/*Controller.groovy',
91                'file:./grails-app/conf/SecurityConfig.groovy'
92        ]
93        Map dependsOn = [:]
94
95        def doWithSpring = {
96
97                def conf = AuthorizeTools.securityConfig.security
98                if (!conf || !conf.active) {
99                        println '[active=false] Spring Security not loaded'
100                        return
101                }
102
103                println 'loading security config ...'
104
105                createRefList.delegate = delegate
106
107                /** springSecurityFilterChain */
108                configureFilterChain.delegate = delegate
109                configureFilterChain conf
110
111                // OpenID
112                if (conf.useOpenId) {
113                        configureOpenId.delegate = delegate
114                        configureOpenId conf
115                }
116
117                // Facebook Connect
118                if (conf.useFacebook) {
119                        configureFacebook.delegate = delegate
120                        configureFacebook conf
121                }
122
123                // logout
124                configureLogout.delegate = delegate
125                configureLogout conf
126
127                // Basic Auth
128                configureBasicAuth.delegate = delegate
129                configureBasicAuth conf
130
131                /** httpSessionContextIntegrationFilter */
132                httpSessionContextIntegrationFilter(HttpSessionContextIntegrationFilter)
133
134                /** authenticationProcessingFilter */
135                authenticationProcessingFilter(GrailsAuthenticationProcessingFilter) {
136                        authenticationManager = ref('authenticationManager')
137                        authenticationFailureUrl = conf.authenticationFailureUrl //'/login/authfail?login_error=1'
138                        ajaxAuthenticationFailureUrl = conf.ajaxAuthenticationFailureUrl // /login/authfail?ajax=true
139                        defaultTargetUrl = conf.defaultTargetUrl // '/'
140                        alwaysUseDefaultTargetUrl = conf.alwaysUseDefaultTargetUrl // false
141                        filterProcessesUrl = conf.filterProcessesUrl // '/j_spring_security_check'
142                        rememberMeServices = ref('rememberMeServices')
143                        authenticateService = ref('authenticateService')
144                }
145
146                /** securityContextHolderAwareRequestFilter */
147                securityContextHolderAwareRequestFilter(SecurityContextHolderAwareRequestFilter) {
148                        portResolver = ref('portResolver')
149                }
150
151                /** rememberMeProcessingFilter */
152                rememberMeProcessingFilter(RememberMeProcessingFilter) {
153                        authenticationManager = ref('authenticationManager')
154                        rememberMeServices = ref('rememberMeServices')
155                }
156
157                /** rememberMeServices */
158                if (conf.useOpenId || conf.useFacebook) {
159                        // auth is external, so no password, so cookie isn't possible
160                        rememberMeServices(NullRememberMeServices)
161                }
162                else {
163                        rememberMeServices(TokenBasedRememberMeServices) {
164                                userDetailsService = ref('userDetailsService')
165                                key = conf.rememberMeKey
166                                cookieName = conf.cookieName
167                                alwaysRemember = conf.alwaysRemember
168                                tokenValiditySeconds = conf.tokenValiditySeconds
169                                parameter = conf.parameter
170                        }
171                }
172
173                /** anonymousProcessingFilter */
174                anonymousProcessingFilter(AnonymousProcessingFilter) {
175                        key = conf.key // 'foo'
176                        userAttribute = conf.userAttribute //'anonymousUser,ROLE_ANONYMOUS'
177                }
178
179                /** exceptionTranslationFilter */
180                exceptionTranslationFilter(ExceptionTranslationFilter) {
181                        authenticationEntryPoint = ref('authenticationEntryPoint')
182                        accessDeniedHandler = ref('accessDeniedHandler')
183                        portResolver = ref('portResolver')
184                }
185                accessDeniedHandler(GrailsAccessDeniedHandlerImpl) {
186                        errorPage = conf.errorPage == 'null' ? null : conf.errorPage // '/login/denied' or 403
187                        ajaxErrorPage = conf.ajaxErrorPage
188                        portResolver = ref('portResolver')
189                        if (conf.ajaxHeader) {
190                                ajaxHeader = conf.ajaxHeader //default: X-Requested-With
191                        }
192                }
193
194                if (!conf.useNtlm) {
195                        authenticationEntryPoint(WithAjaxAuthenticationProcessingFilterEntryPoint) {
196                                loginFormUrl = conf.loginFormUrl // '/login/auth'
197                                forceHttps = conf.forceHttps // 'false'
198                                ajaxLoginFormUrl = conf.ajaxLoginFormUrl // '/login/authAjax'
199                                if (conf.ajaxHeader) {
200                                        ajaxHeader = conf.ajaxHeader //default: X-Requested-With
201                                }
202                                portMapper = ref('portMapper')
203                                portResolver = ref('portResolver')
204                        }
205                }
206
207                // voters
208                configureVoters.delegate = delegate
209                configureVoters conf
210
211                /** filterInvocationInterceptor */
212                filterInvocationInterceptor(FilterSecurityInterceptor) {
213                        authenticationManager = ref('authenticationManager')
214                        accessDecisionManager = ref('accessDecisionManager')
215                        if (conf.useControllerAnnotations || conf.useRequestMapDomainClass) {
216                                objectDefinitionSource = ref('objectDefinitionSource')
217                        }
218                        else {
219                                objectDefinitionSource = conf.requestMapString
220                        }
221                }
222                if (conf.useControllerAnnotations) {
223                        objectDefinitionSource(AnnotationFilterInvocationDefinition) {
224                                boolean lowercase = conf.controllerAnnotationsMatchesLowercase
225                                if ('ant'.equals(conf.controllerAnnotationsMatcher)) {
226                                        urlMatcher = new AntUrlPathMatcher(lowercase)
227                                }
228                                else {
229                                        urlMatcher = new RegexUrlPathMatcher(lowercase)
230                                }
231                                if (conf.controllerAnnotationsRejectIfNoRule instanceof Boolean) {
232                                        rejectIfNoRule = conf.controllerAnnotationsRejectIfNoRule
233                                }
234                        }
235                }
236                else if (conf.useRequestMapDomainClass) {
237                        objectDefinitionSource(RequestmapFilterInvocationDefinition) {
238                                requestMapClass = conf.requestMapClass
239                                requestMapPathFieldName = conf.requestMapPathField
240                                requestMapConfigAttributeField = conf.requestMapConfigAttributeField
241                                urlMatcher = new AntUrlPathMatcher(true)
242                        }
243                }
244
245                /** anonymousAuthenticationProvider */
246                anonymousAuthenticationProvider(AnonymousAuthenticationProvider) {
247                        key = conf.key // 'foo'
248                }
249                /** rememberMeAuthenticationProvider */
250                rememberMeAuthenticationProvider(RememberMeAuthenticationProvider) {
251                        key = conf.rememberMeKey
252                }
253
254                // authenticationManager
255                configureAuthenticationManager.delegate = delegate
256                configureAuthenticationManager conf
257
258                /** daoAuthenticationProvider */
259                daoAuthenticationProvider(GrailsDaoAuthenticationProvider) {
260                        userDetailsService = ref('userDetailsService')
261                        passwordEncoder = ref('passwordEncoder')
262                        userCache = ref('userCache')
263                }
264
265                /** passwordEncoder */
266                passwordEncoder(MessageDigestPasswordEncoder, conf.algorithm) {
267                        if (conf.encodeHashAsBase64) {
268                                encodeHashAsBase64 = true
269                        }
270                }
271
272                // user details cache
273                if (conf.cacheUsers) {
274                        userCache(EhCacheBasedUserCache) {
275                                cache = ref('securityUserCache')
276                        }
277                        securityUserCache(EhCacheFactoryBean) {
278                                cacheManager = ref('cacheManager')
279                                cacheName = 'userCache'
280                        }
281                        cacheManager(EhCacheManagerFactoryBean)
282                }
283                else {
284                        userCache(NullUserCache)
285                }
286
287                /** userDetailsService */
288                userDetailsService(GrailsDaoImpl) {
289                        usernameFieldName = conf.userName
290                        passwordFieldName = conf.password
291                        enabledFieldName = conf.enabled
292                        authorityFieldName = conf.authorityField
293                        loginUserDomainClass = conf.loginUserDomainClass
294                        relationalAuthoritiesField = conf.relationalAuthorities
295                        authoritiesMethodName = conf.getAuthoritiesMethod
296                        sessionFactory = ref('sessionFactory')
297                        authenticateService = ref('authenticateService')
298                }
299
300                /** loggerListener ( log4j.logger.org.springframework.security=info,stdout ) */
301                if (conf.useLogger) {
302                        loggerListener(LoggerListener)
303                }
304
305                // port mappings for channel security, etc.
306                portMapper(PortMapperImpl) {
307                        portMappings = [(conf.httpPort.toString()) : conf.httpsPort.toString()]
308                }
309                portResolver(PortResolverImpl) {
310                        portMapper = portMapper
311                }
312
313                daacc(DefaultAdvisorAutoProxyCreator)
314
315                // experiment on Annotation and MethodSecurityInterceptor for secure services
316                configureAnnotatedServices.delegate = delegate
317                configureAnnotatedServices conf
318
319                // simple email service
320                configureMail.delegate = delegate
321                configureMail conf
322
323                // Switch User
324                if (conf.switchUserProcessingFilter) {
325                        switchUserProcessingFilter(SwitchUserProcessingFilter) {
326                                userDetailsService = ref('userDetailsService')
327                                switchUserUrl = conf.swswitchUserUrl
328                                exitUserUrl = conf.swexitUserUrl
329                                targetUrl = conf.swtargetUrl
330                        }
331                }
332
333                // LDAP
334                if (conf.useLdap) {
335                        configureLdap.delegate = delegate
336                        configureLdap conf
337                }
338
339                // SecurityEventListener
340                securityEventListener(SecurityEventListener) {
341                        authenticateService = ref('authenticateService')
342                }
343
344                // Kerberos
345                if (conf.useKerberos) {
346                        configureKerberos.delegate = delegate
347                        configureKerberos conf
348                }
349
350                // NTLM
351                if (conf.useNtlm) {
352                        configureNtlm.delegate = delegate
353                        configureNtlm conf
354                }
355
356                // CAS
357                if (conf.useCAS) {
358                        configureCAS.delegate = delegate
359                        configureCAS conf
360                }
361
362                // channel (http/https) security
363                if (useSecureChannel(conf)) {
364                        configureChannelProcessingFilter.delegate = delegate
365                        configureChannelProcessingFilter conf
366                }
367
368                // IP filter
369                if (conf.ipRestrictions) {
370                        configureIpFilter.delegate = delegate
371                        configureIpFilter conf
372                }
373        }
374
375        private boolean useSecureChannel(conf) {
376                return conf.secureChannelDefinitionSource || conf.channelConfig.secure || conf.channelConfig.insecure
377        }
378
379        // OpenID
380        private def configureOpenId = { conf ->
381                openIDAuthProvider(org.codehaus.groovy.grails.plugins.springsecurity.openid.GrailsOpenIdAuthenticationProvider) {
382                        userDetailsService = ref('userDetailsService')
383                }
384                openIDStore(org.openid4java.consumer.InMemoryConsumerAssociationStore)
385                openIDNonceVerifier(org.openid4java.consumer.InMemoryNonceVerifier, conf.openIdNonceMaxSeconds) // 300 seconds
386                openIDConsumerManager(org.openid4java.consumer.ConsumerManager) {
387                        nonceVerifier = openIDNonceVerifier
388                }
389                openIDConsumer(org.springframework.security.ui.openid.consumers.OpenID4JavaConsumer, openIDConsumerManager)
390                openIDAuthenticationProcessingFilter(org.springframework.security.ui.openid.OpenIDAuthenticationProcessingFilter) {
391                        authenticationManager = ref('authenticationManager')
392                        authenticationFailureUrl = conf.authenticationFailureUrl //'/login/authfail?login_error=1' // /spring_security_login?login_error
393                        defaultTargetUrl = conf.defaultTargetUrl // '/'
394                        filterProcessesUrl = '/j_spring_openid_security_check' // not configurable
395                        rememberMeServices = ref('rememberMeServices')
396                        consumer = openIDConsumer
397                }
398        }
399
400        // Facebook
401        private def configureFacebook = { conf ->
402                facebookAuthProvider(org.codehaus.groovy.grails.plugins.springsecurity.facebook.FacebookAuthenticationProvider) {
403                        userDetailsService = ref('userDetailsService')
404                }
405                facebookAuthenticationProcessingFilter(org.codehaus.groovy.grails.plugins.springsecurity.facebook.FacebookAuthenticationProcessingFilter) {
406                        authenticationManager = ref('authenticationManager')
407                        authenticationFailureUrl = conf.authenticationFailureUrl //'/login/authfail?login_error=1' // /spring_security_login?login_error
408                        defaultTargetUrl = conf.defaultTargetUrl // '/'
409                        filterProcessesUrl = conf.facebook.filterProcessesUrl // '/j_spring_facebook_security_check'
410                        apiKey = conf.facebook.apiKey
411                        secretKey = conf.facebook.secretKey
412                        authenticationUrlRoot = conf.facebook.authenticationUrlRoot // http://www.facebook.com/login.php?v=1.0&api_key=
413                        rememberMeServices = ref('rememberMeServices')
414                }
415                facebookLogoutHandler(org.codehaus.groovy.grails.plugins.springsecurity.facebook.FacebookLogoutHandler) {
416                        apiKey = conf.facebook.apiKey
417                }
418        }
419
420        private def configureCAS = { conf ->
421                String casHost = conf.cas.casServer ?: 'localhost'
422                int casPort = (conf.cas.casServerPort ?: '443').toInteger()
423                String casFilterProcessesUrl = conf.cas.filterProcessesUrl ?: '/j_spring_cas_security_check'
424                boolean sendRenew = Boolean.valueOf(conf.cas.sendRenew ?: false)
425                String proxyReceptorUrl = conf.cas.proxyReceptorUrl ?: '/secure/receptor'
426                String applicationHost = System.getProperty('server.host') ?: 'localhost'
427                int applicationPort = (System.getProperty('server.port') ?: 8080).toInteger()
428                String appName = application.metadata['app.name']
429                String casHttp = conf.cas.casServerSecure ? 'https' : 'http'
430                String localHttp = conf.cas.localhostSecure ? 'https' : 'http'
431
432                proxyGrantingTicketStorage(org.jasig.cas.client.proxy.ProxyGrantingTicketStorageImpl)
433
434                casProcessingFilter(org.springframework.security.ui.cas.CasProcessingFilter) {
435                        authenticationManager = ref('authenticationManager')
436                        authenticationFailureUrl = conf.cas.failureURL ?: '/denied.jsp'
437                        defaultTargetUrl = conf.cas.defaultTargetURL ?: '/'
438                        filterProcessesUrl = casFilterProcessesUrl
439                        proxyGrantingTicketStorage = proxyGrantingTicketStorage
440                        proxyReceptorUrl = proxyReceptorUrl
441                }
442
443                casServiceProperties(org.springframework.security.ui.cas.ServiceProperties) {
444                        service = "$localHttp://$applicationHost:$applicationPort/$appName$casFilterProcessesUrl"
445                        sendRenew = sendRenew
446                }
447
448                String casLoginURL = conf.cas.fullLoginURL ?: "$casHttp://$casHost:$casPort/cas/login"
449                authenticationEntryPoint(org.springframework.security.ui.cas.CasProcessingFilterEntryPoint) {
450                        loginUrl = casLoginURL
451                        serviceProperties = casServiceProperties
452                }
453
454                String casServiceURL = conf.cas.fullServiceURL ?: "$casHttp://$casHost:$casPort/cas"
455                cas20ServiceTicketValidator(org.jasig.cas.client.validation.Cas20ServiceTicketValidator, casServiceURL) {
456                        proxyGrantingTicketStorage = proxyGrantingTicketStorage
457                        proxyCallbackUrl = "$localHttp://$applicationHost:$applicationPort/$appName$proxyReceptorUrl"
458                }
459
460                // the CAS authentication provider key doesn't need to be anything special, it just identifies individual providers
461                // so that they can identify tokens it previously authenticated
462                String casAuthenticationProviderKey = conf.cas.authenticationProviderKey ?: appName + System.currentTimeMillis()
463                casAuthenticationProvider(org.springframework.security.providers.cas.CasAuthenticationProvider) {
464                        userDetailsService = ref(conf.cas.userDetailsService ?: 'userDetailsService')
465                        serviceProperties = casServiceProperties
466                        ticketValidator = cas20ServiceTicketValidator
467                        key = casAuthenticationProviderKey
468                }
469        }
470
471        private def configureLogout = { conf ->
472
473                securityContextLogoutHandler(SecurityContextLogoutHandler)
474                def logoutHandlerNames = conf.logoutHandlerNames
475                if (!logoutHandlerNames) {
476                        logoutHandlerNames = []
477                        if (conf.useFacebook) {
478                                logoutHandlerNames << 'facebookLogoutHandler'
479                        }
480                        else if (!conf.useOpenId) {
481                                logoutHandlerNames << 'rememberMeServices'
482                        }
483                        logoutHandlerNames << 'securityContextLogoutHandler'
484                }
485
486                def logoutHandlers = createRefList(logoutHandlerNames)
487                def afterLogoutUrl = conf.afterLogoutUrl // '/'
488
489                /** logoutFilter */
490                logoutFilter(LogoutFilterFactoryBean) {
491                        logoutSuccessUrl = afterLogoutUrl
492                        handlers = logoutHandlers
493                }
494        }
495
496        private def configureBasicAuth = { conf ->
497
498                basicProcessingFilterEntryPoint(BasicProcessingFilterEntryPoint) {
499                        realmName = conf.realmName // 'Grails Realm'
500                }
501                basicProcessingFilter(BasicProcessingFilter) {
502                        authenticationManager = ref('authenticationManager')
503                        authenticationEntryPoint = basicProcessingFilterEntryPoint
504                }
505        }
506
507        private def configureVoters = { conf ->
508
509                roleVoter(RoleVoter)
510
511                authenticatedVoter(AuthenticatedVoter)
512
513                def decisionVoterNames = conf.decisionVoterNames
514                if (!decisionVoterNames) {
515                        decisionVoterNames = ['authenticatedVoter', 'roleVoter']
516                }
517                def decisionVoterList = createRefList(decisionVoterNames)
518                /** accessDecisionManager */
519                accessDecisionManager(AuthenticatedVetoableDecisionManager) {
520                        allowIfAllAbstainDecisions = false
521                        decisionVoters = decisionVoterList
522                }
523        }
524
525        private def configureAuthenticationManager = { conf ->
526
527                def providerNames = conf.providerNames
528                if (!providerNames) {
529                        providerNames = []
530                        if (conf.useKerberos) {
531                                providerNames << 'kerberosAuthProvider'
532                        }
533                        if (conf.useCAS) {
534                                providerNames << 'casAuthenticationProvider'
535                        }
536                        if (conf.useLdap) {
537                                providerNames << 'ldapAuthProvider'
538                        }
539                        if (conf.useFacebook) {
540                                providerNames << 'facebookAuthProvider'
541                        }
542
543                        if (providerNames.empty) {
544                                providerNames << 'daoAuthenticationProvider'
545                                if (conf.useOpenId) {
546                                        providerNames << 'openIDAuthProvider'
547                                }
548                        }
549
550                        providerNames << 'anonymousAuthenticationProvider'
551                        providerNames << 'rememberMeAuthenticationProvider'
552                }
553
554                def providerList = createRefList(providerNames)
555                /** authenticationManager */
556                authenticationManager(ProviderManager) {
557                        providers = providerList
558                }
559        }
560
561        private def configureAnnotatedServices = { conf ->
562
563                serviceSecureAnnotation(SecurityAnnotationAttributes)
564
565                serviceSecureAnnotationODS(MethodDefinitionAttributes) {
566                        attributes = serviceSecureAnnotation
567                }
568
569                /** securityInteceptor */
570                securityInteceptor(QuietMethodSecurityInterceptor) {
571                        validateConfigAttributes = false
572                        authenticationManager = ref('authenticationManager')
573                        accessDecisionManager = ref('accessDecisionManager')
574                        objectDefinitionSource = serviceSecureAnnotationODS
575                        throwException = true
576                }
577
578                // load Services which have Annotations
579                application.serviceClasses.each { serviceClass ->
580                        if (hasAnnotation(serviceClass.clazz)) {
581                                "${serviceClass.propertyName}Sec"(BeanNameAutoProxyCreator) {
582                                        beanNames = serviceClass.propertyName
583                                        interceptorNames = ['securityInteceptor']
584                                        proxyTargetClass = true
585                                }
586                        }
587                }
588        }
589
590        private def configureMail = { conf ->
591
592                if (conf.useMail) {
593                        mailSender(org.springframework.mail.javamail.JavaMailSenderImpl) {
594                                host = conf.mailHost
595                                username = conf.mailUsername
596                                password = conf.mailPassword
597                                protocol = conf.mailProtocol
598                                port = conf.mailPort
599                                if (conf.javaMailProperties) {
600                                        javaMailProperties = conf.javaMailProperties as Properties
601                                }
602                        }
603
604                        mailMessage(org.springframework.mail.SimpleMailMessage) {
605                                from = conf.mailFrom
606                        }
607                }
608        }
609
610        private def configureLdap = { conf ->
611
612                contextSource(org.springframework.security.ldap.DefaultSpringSecurityContextSource, conf.ldapServer) {
613                        userDn = conf.ldapManagerDn
614                        password = conf.ldapManagerPassword
615                }
616
617                ldapUserSearch(org.springframework.security.ldap.search.FilterBasedLdapUserSearch,
618                                   conf.ldapSearchBase, conf.ldapSearchFilter, contextSource) {
619                        searchSubtree = conf.ldapSearchSubtree
620                }
621
622                ldapAuthenticator(org.springframework.security.providers.ldap.authenticator.BindAuthenticator,
623                                      contextSource) {
624                        userSearch = ldapUserSearch
625                }
626
627                ldapUserDetailsMapper(org.codehaus.groovy.grails.plugins.springsecurity.ldap.GrailsLdapUserDetailsMapper) {
628                        userDetailsService = ref('userDetailsService')
629                        passwordAttributeName = conf.ldapPasswordAttributeName // 'userPassword'
630                        usePassword = conf.ldapUsePassword // true
631                        retrieveDatabaseRoles = conf.ldapRetrieveDatabaseRoles // false
632                }
633
634                if (conf.ldapRetrieveGroupRoles) {
635                        ldapAuthoritiesPopulator(org.springframework.security.ldap.populator.DefaultLdapAuthoritiesPopulator,
636                                                     contextSource, conf.ldapGroupSearchBase) {
637                                groupRoleAttribute = conf.ldapGroupRoleAttribute
638                                groupSearchFilter = conf.ldapGroupSearchFilter
639                                searchSubtree = conf.ldapSearchSubtree
640                        }
641                        ldapAuthProvider(org.springframework.security.providers.ldap.LdapAuthenticationProvider,
642                                             ldapAuthenticator, ldapAuthoritiesPopulator) {
643                                userDetailsContextMapper = ldapUserDetailsMapper
644                        }
645                }
646                else {
647                        // use the NullAuthoritiesPopulator
648                        ldapAuthProvider(org.springframework.security.providers.ldap.LdapAuthenticationProvider,
649                                             ldapAuthenticator) {
650                                userDetailsContextMapper = ldapUserDetailsMapper
651                        }
652                }
653        }
654
655        private def configureKerberos = { conf ->
656
657                jaasNameCallbackHandler(org.springframework.security.providers.jaas.JaasNameCallbackHandler)
658
659                jaasPasswordCallbackHandler(org.springframework.security.providers.jaas.JaasPasswordCallbackHandler)
660
661                kerberosAuthProvider(org.codehaus.groovy.grails.plugins.springsecurity.kerberos.GrailsKerberosAuthenticationProvider) {
662                        authenticateService = ref('authenticateService')
663                        userDetailsService = ref('userDetailsService')
664                        loginConfig = conf.kerberosLoginConfigFile
665                        loginContextName = 'KrbAuthentication'
666                        callbackHandlers = [jaasNameCallbackHandler, jaasPasswordCallbackHandler]
667                        authorityGranters = []
668                }
669
670                //TODO: Improve
671                System.setProperty('java.security.krb5.realm', conf.kerberosRealm)
672                System.setProperty('java.security.krb5.kdc', conf.kerberosKdc)
673        }
674
675        private def configureNtlm = { conf ->
676
677                ntlmFilter(org.springframework.security.ui.ntlm.NtlmProcessingFilter) {
678                        stripDomain = conf.ntlm.stripDomain // true
679                        retryOnAuthFailure = conf.ntlm.retryOnAuthFailure // true
680                        defaultDomain = conf.ntlm.defaultDomain
681                        netbiosWINS = conf.ntlm.netbiosWINS
682                        forceIdentification = conf.ntlm.forceIdentification // false
683                        authenticationManager = ref('authenticationManager')
684                }
685
686                authenticationEntryPoint(org.codehaus.groovy.grails.plugins.springsecurity.GrailsNtlmProcessingFilterEntryPoint) {
687                        authenticationFailureUrl = conf.authenticationFailureUrl
688                }
689        }
690
691        private def configureFilterChain = { conf ->
692
693                def filterNames = conf.filterNames
694                if (!filterNames) {
695                        filterNames = []
696
697                        if (useSecureChannel(conf)) {
698                                filterNames << 'channelProcessingFilter' // CHANNEL_FILTER
699                        }
700
701                        // CONCURRENT_SESSION_FILTER
702
703                        filterNames << 'httpSessionContextIntegrationFilter' // HTTP_SESSION_CONTEXT_FILTER
704
705                        filterNames << 'logoutFilter' // LOGOUT_FILTER
706
707                        if (conf.ipRestrictions) {
708                                filterNames << 'ipAddressFilter'
709                        }
710
711                        // X509_FILTER
712
713                        // PRE_AUTH_FILTER
714
715                        if (conf.useCAS) {
716                                filterNames << 'casProcessingFilter' // CAS_PROCESSING_FILTER
717                        }
718
719                        filterNames << 'authenticationProcessingFilter' // AUTHENTICATION_PROCESSING_FILTER
720
721                        if (conf.useOpenId) {
722                                filterNames << 'openIDAuthenticationProcessingFilter' // OPENID_PROCESSING_FILTER
723                        }
724
725                        if (conf.useFacebook) {
726                                filterNames << 'facebookAuthenticationProcessingFilter'
727                        }
728
729                        // LOGIN_PAGE_FILTER
730
731                        if (conf.basicProcessingFilter) {
732                                filterNames << 'basicProcessingFilter' // BASIC_PROCESSING_FILTER
733                        }
734
735                        if (!conf.useNtlm) {
736                                // seems to remove NTLM authentication tokens
737                                filterNames << 'securityContextHolderAwareRequestFilter' // SERVLET_API_SUPPORT_FILTER
738                        }
739
740                        filterNames << 'rememberMeProcessingFilter' // REMEMBER_ME_FILTER
741
742                        filterNames << 'anonymousProcessingFilter' // ANONYMOUS_FILTER
743
744                        filterNames << 'exceptionTranslationFilter' // EXCEPTION_TRANSLATION_FILTER
745
746                        if (conf.useNtlm) {
747                                filterNames << 'ntlmFilter' // NTLM_FILTER
748                        }
749
750                        // SESSION_FIXATION_FILTER
751
752                        filterNames << 'filterInvocationInterceptor' // FILTER_SECURITY_INTERCEPTOR
753
754                        if (conf.switchUserProcessingFilter) {
755                                filterNames << 'switchUserProcessingFilter' // SWITCH_USER_FILTER
756                        }
757                }
758                String joinedFilters = filterNames.join(',')
759
760                String definitionSource
761                if (conf.filterInvocationDefinitionSource) {
762                        // if the entire string is set in the config, use that
763                        definitionSource = conf.filterInvocationDefinitionSource
764                }
765                else if (conf.filterInvocationDefinitionSourceMap) {
766                        // otherwise if there's a map of configs, use those
767                        definitionSource = DEFINITION_SOURCE_PREFIX
768                        conf.filterInvocationDefinitionSourceMap.each { key, value ->
769                                if (value == 'JOINED_FILTERS') {
770                                        // special case to use either the filters defined by
771                                        // conf.filterNames or the filters defined by config settings
772                                        value = joinedFilters
773                                }
774                                definitionSource += "$key=$value\n"
775                        }
776                }
777                else {
778                        // otherwise build the default string - all urls guarded by all filters
779                        definitionSource = "$DEFINITION_SOURCE_PREFIX\n/**=$joinedFilters"
780                }
781                springSecurityFilterChain(FilterChainProxy) {
782                        filterInvocationDefinitionSource = definitionSource
783                }
784        }
785
786        private def configureChannelProcessingFilter = { conf ->
787
788                retryWithHttpEntryPoint(RetryWithHttpEntryPoint) {
789                        portMapper = ref('portMapper')
790                        portResolver = ref('portResolver')
791                }
792
793                retryWithHttpsEntryPoint(RetryWithHttpsEntryPoint) {
794                        portMapper = ref('portMapper')
795                        portResolver = ref('portResolver')
796                }
797
798                secureChannelProcessor(SecureChannelProcessor) {
799                        entryPoint = retryWithHttpsEntryPoint
800                }
801
802                insecureChannelProcessor(InsecureChannelProcessor) {
803                        entryPoint = retryWithHttpEntryPoint
804                }
805
806                channelDecisionManager(ChannelDecisionManagerImpl) {
807                        channelProcessors = [insecureChannelProcessor, secureChannelProcessor]
808                }
809
810                String definitionSource
811                if (conf.secureChannelDefinitionSource) {
812                        // if the entire string is set in the config, use that
813                        definitionSource = conf.secureChannelDefinitionSource
814                }
815                else {
816                        definitionSource = DEFINITION_SOURCE_PREFIX
817                        conf.channelConfig.secure.each { pattern ->
818                                definitionSource += "$pattern=REQUIRES_SECURE_CHANNEL\n"
819                        }
820                        conf.channelConfig.insecure.each { pattern ->
821                                definitionSource += "$pattern=REQUIRES_INSECURE_CHANNEL\n"
822                        }
823                }
824                channelProcessingFilter(ChannelProcessingFilter) {
825                        channelDecisionManager = channelDecisionManager
826                        filterInvocationDefinitionSource = definitionSource
827                }
828        }
829
830        private def configureIpFilter = { conf ->
831                ipAddressFilter(IpAddressFilter) {
832                        ipRestrictions = conf.ipRestrictions
833                }
834        }
835
836        def doWithApplicationContext = { ctx ->
837                // nothing to do
838        }
839
840        def doWithWebDescriptor = { xml ->
841
842                def conf = AuthorizeTools.securityConfig.security
843                if (!conf || !conf.active) {
844                        return
845                }
846
847                // we add the filter(s) right after the last context-param
848                def contextParam = xml.'context-param'
849
850                // the name of the filter matches the name of the Spring bean that it delegates to
851                contextParam[contextParam.size() - 1] + {
852                        'filter' {
853                                'filter-name'('springSecurityFilterChain')
854                                'filter-class'(DelegatingFilterProxy.name)
855                        }
856                }
857
858                // add the filter-mapping after the Spring character encoding filter
859                findMappingLocation.delegate = delegate
860                def mappingLocation = findMappingLocation(xml)
861                mappingLocation + {
862                        'filter-mapping'{
863                                'filter-name'('springSecurityFilterChain')
864                                'url-pattern'('/*')
865                        }
866                }
867
868                if (conf.useHttpSessionEventPublisher) {
869                        def filterMapping = xml.'filter-mapping'
870                        filterMapping[filterMapping.size() - 1] + {
871                                'listener' {
872                                        'listener-class'(HttpSessionEventPublisher.name)
873                                }
874                        }
875                }
876        }
877
878        private def findMappingLocation = { xml ->
879
880                // find the location to insert the filter-mapping; needs to be after the 'charEncodingFilter'
881                // which may not exist. should also be before the sitemesh filter.
882                // thanks to the JSecurity plugin for the logic.
883
884                def mappingLocation = xml.'filter-mapping'.find { it.'filter-name'.text() == 'charEncodingFilter' }
885                if (mappingLocation) {
886                        return mappingLocation
887                }
888
889                // no 'charEncodingFilter'; try to put it before sitemesh
890                int i = 0
891                int siteMeshIndex = -1
892                xml.'filter-mapping'.each {
893                        if (it.'filter-name'.text().equalsIgnoreCase('sitemesh')) {
894                                siteMeshIndex = i
895                        }
896                        i++
897                }
898                if (siteMeshIndex > 0) {
899                        return xml.'filter-mapping'[siteMeshIndex - 1]
900                }
901
902                if (siteMeshIndex == 0 || xml.'filter-mapping'.size() == 0) {
903                        def filters = xml.'filter'
904                        return filters[filters.size() - 1]
905                }
906
907                // neither filter found
908                def filters = xml.'filter'
909                return filters[filters.size() - 1]
910        }
911
912        def doWithDynamicMethods = { ctx ->
913
914                def conf = AuthorizeTools.securityConfig.security
915                if (!conf || !conf.active) {
916                        return
917                }
918
919                for (controllerClass in application.controllerClasses) {
920                        addControllerMethods controllerClass.metaClass
921                }
922
923                if (conf.useControllerAnnotations) {
924                        ctx.objectDefinitionSource.initialize conf.controllerAnnotationStaticRules,
925                                ctx.grailsUrlMappingsHolder, application.controllerClasses
926                }
927        }
928
929        def onChange = { event ->
930
931                def conf = AuthorizeTools.securityConfig.security
932                if (!conf || !conf.active) {
933                        return
934                }
935
936                def ctx = event.ctx
937                if (event.source && ctx && event.application) {
938                        boolean isControllerClass = application.isControllerClass(event.source)
939                        boolean configChanged = 'SecurityConfig'.equals(event.source.name)
940                        if (configChanged || isControllerClass) {
941                                if (conf.useControllerAnnotations) {
942                                        ctx.objectDefinitionSource.initialize conf.controllerAnnotationStaticRules,
943                                                ctx.grailsUrlMappingsHolder, application.controllerClasses
944                                }
945                                if (isControllerClass) {
946                                        addControllerMethods application.getControllerClass(event.source.name).metaClass
947                                }
948                        }
949                }
950        }
951
952        def onApplicationChange = { event ->
953                // nothing to do
954        }
955
956        private void addControllerMethods(MetaClass mc) {
957                mc.getAuthUserDomain = {
958                        def principal = SCH.context?.authentication?.principal
959                        if (principal != null && principal != 'anonymousUser') {
960                                return principal?.domainClass
961                        }
962
963                        return null
964                }
965
966                mc.getPrincipalInfo = {
967                        return SCH.context?.authentication?.principal
968                }
969
970                mc.isUserLogon = {
971                        def principal = SCH.context?.authentication?.principal
972                        return principal != null && principal != 'anonymousUser'
973                }
974        }
975
976        private boolean hasAnnotation(serviceClass) {
977                for (method in serviceClass.methods) {
978                        if (method.isAnnotationPresent(SecuredService)) {
979                                return true
980                        }
981                }
982
983                return false
984        }
985
986        private def createRefList = { names ->
987                names.collect { name -> ref(name) }
988        }
989}
Note: See TracBrowser for help on using the repository browser.