removed spring security, replaced with lightweight configurable 
SecurityProvider interface.
removed unnecessary transaction support.
updated WebAppRunnerTest to use new security provider mechanism.
overall reduces `mvn clean install` time from 1m15s for console to 1m, and from 
1m20s to 45s for launcher, hurrah!


Project: http://git-wip-us.apache.org/repos/asf/brooklyn-ui/repo
Commit: http://git-wip-us.apache.org/repos/asf/brooklyn-ui/commit/d3c8046e
Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-ui/tree/d3c8046e
Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-ui/diff/d3c8046e

Branch: refs/heads/0.4.0
Commit: d3c8046ebded164853a91376eb5ed52142a92cc6
Parents: bce56d9 3ca81e1
Author: Alex Heneveld <[email protected]>
Authored: Sat May 5 19:49:33 2012 +0300
Committer: Alex Heneveld <[email protected]>
Committed: Sat May 5 20:28:17 2012 +0300

----------------------------------------------------------------------
 usage/web-console/application.properties        |   5 +-
 .../grails-app/conf/BootStrap.groovy            |  28 --
 usage/web-console/grails-app/conf/Config.groovy |  18 +-
 .../grails-app/conf/SecurityFilters.groovy      |  20 ++
 .../grails-app/conf/UrlMappings.groovy          |  10 +
 .../web/console/DashboardController.groovy      |   2 +-
 .../web/console/EntityController.groovy         |   3 +-
 .../brooklyn/web/console/LoginController.groovy | 153 ++++------
 .../web/console/LogoutController.groovy         |   7 +-
 .../brooklyn/web/console/SecurityRole.groovy    |  14 -
 .../brooklyn/web/console/SecurityUser.groovy    |  24 --
 .../web/console/SecurityUserRole.groovy         |  52 ----
 .../web/console/ManagementContextService.groovy |   4 +-
 .../web/console/TestWebApplication.groovy       | 276 -------------------
 .../web/console/TestWebApplication.groovy       | 276 +++++++++++++++++++
 .../security/AnyoneSecurityProvider.java        |  23 ++
 .../security/BlackholeSecurityProvider.java     |  23 ++
 .../web/console/security/ConfigLoader.java      |  36 +++
 .../security/DelegatingSecurityProvider.java    |  49 ++++
 .../security/ExplicitUsersSecurityProvider.java |  98 +++++++
 .../web/console/security/SecurityProvider.java  |  12 +
 .../console/security/WebConsoleSecurity.java    |  12 +
 .../web-console/grails-app/views/login/auth.gsp |   2 +-
 usage/web-console/pom.xml                       |   6 +-
 24 files changed, 632 insertions(+), 521 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/d3c8046e/usage/web-console/application.properties
----------------------------------------------------------------------
diff --cc usage/web-console/application.properties
index e271f9e,e271f9e..fb668c8
--- a/usage/web-console/application.properties
+++ b/usage/web-console/application.properties
@@@ -1,11 -1,11 +1,8 @@@
  #Grails Metadata file
--#Wed Aug 03 12:12:25 BST 2011
++#Sat May 05 09:11:19 PDT 2012
  app.grails.version=1.3.7
  app.name=brooklyn-web-console
  app.servlet.version=2.4
--# BROOKLYN_VERSION_BELOW
  app.version=0.4.0-SNAPSHOT
  plugins.haml=0.3
--plugins.hibernate=1.3.7
--plugins.spring-security-core=1.1.3
  plugins.tomcat=1.3.7

http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/d3c8046e/usage/web-console/grails-app/conf/BootStrap.groovy
----------------------------------------------------------------------
diff --cc usage/web-console/grails-app/conf/BootStrap.groovy
index dfc60da,dfc60da..760a1ef
--- a/usage/web-console/grails-app/conf/BootStrap.groovy
+++ b/usage/web-console/grails-app/conf/BootStrap.groovy
@@@ -1,36 -1,36 +1,8 @@@
  import org.codehaus.groovy.grails.web.context.ServletContextHolder;
  
--import brooklyn.web.console.SecurityRole
--import brooklyn.web.console.SecurityUser
--import brooklyn.web.console.SecurityUserRole
--
  class BootStrap {
--    def springSecurityService
  
--    // TODO hard-coded admin/password account!  (password can be passed in, 
but that's still far from ideal!)
      def init = { servletContext ->
--        String password = 
ServletContextHolder.servletContext?.getAttribute("brooklynbrooklynWeb") ?:
--            'password'
--        
--        def adminRole = SecurityRole.findByAuthority('ROLE_ADMIN') ?: new 
SecurityRole(authority: 'ROLE_ADMIN').save(failOnError: true)
--        def adminUser =  SecurityUser.findByUsername('admin') ?: new 
SecurityUser(
--                username: 'admin',
--                password: springSecurityService.encodePassword(password),
--                enabled: true).save(failOnError: true)
--
--        if (!adminUser.authorities.contains(adminRole)) {
--            SecurityUserRole.create adminUser, adminRole
--        }
--
--        def userRole =  SecurityRole.findByAuthority('ROLE_USER') ?: new 
SecurityRole(authority: 'ROLE_USER').save(failOnError: true)
--        def user = SecurityUser.findByUsername('user') ?: new SecurityUser(
--                username: 'user',
--                password: springSecurityService.encodePassword(password),
--                enabled: true).save(failOnError: true)
--
--        if (!user.authorities.contains(userRole)) {
--            SecurityUserRole.create user, userRole
--        }
      }
  
      def destroy = {

http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/d3c8046e/usage/web-console/grails-app/conf/Config.groovy
----------------------------------------------------------------------
diff --cc usage/web-console/grails-app/conf/Config.groovy
index 6e7d1b0,6e7d1b0..32e89c6
--- a/usage/web-console/grails-app/conf/Config.groovy
+++ b/usage/web-console/grails-app/conf/Config.groovy
@@@ -87,20 -87,20 +87,6 @@@ log4j = 
             'net.sf.ehcache.hibernate'
  
      warn   'org.mortbay.log'
--}
--
--// Added by the Spring Security Core plugin:
--grails.plugins.springsecurity.userLookup.userDomainClassName = 
'brooklyn.web.console.SecurityUser'
--grails.plugins.springsecurity.userLookup.authorityJoinClassName = 
'brooklyn.web.console.SecurityUserRole'
--grails.plugins.springsecurity.authority.className = 
'brooklyn.web.console.SecurityRole'
--
--grails.plugins.springsecurity.useBasicAuth = true
--grails.plugins.springsecurity.basic.realmName = "Brooklyn Web Console"
  
--grails.plugins.springsecurity.successHandler.defaultTargetUrl = "/dashboard"
--
--grails.plugins.springsecurity.controllerAnnotations.staticRules = [
--   '/dashboard/**': ['ROLE_ADMIN'],
--   '/detail/**': ['ROLE_ADMIN'],
--   '/entity/**': ['ROLE_ADMIN']
--]
++    info   'brooklyn'
++}

http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/d3c8046e/usage/web-console/grails-app/conf/SecurityFilters.groovy
----------------------------------------------------------------------
diff --cc usage/web-console/grails-app/conf/SecurityFilters.groovy
index 0000000,0000000..07ee733
new file mode 100644
--- /dev/null
+++ b/usage/web-console/grails-app/conf/SecurityFilters.groovy
@@@ -1,0 -1,0 +1,20 @@@
++import org.slf4j.Logger
++import org.slf4j.LoggerFactory
++
++import brooklyn.web.console.security.WebConsoleSecurity
++
++class SecurityFilters {
++   public static final Logger log = 
LoggerFactory.getLogger(SecurityFilters.class);
++   
++   def filters = {
++       loginCheck(controller:'*', action:'*') {
++           before = {
++              if (!WebConsoleSecurity.getInstance().isAuthenticated(session) 
&& !controllerName.equals('login')) {
++                  log.info("redirecting ${session} from ${controllerName} to 
login page")
++                  redirect(controller:'login')
++                  return false
++               }
++           }
++       }
++   }
++}

http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/d3c8046e/usage/web-console/grails-app/conf/UrlMappings.groovy
----------------------------------------------------------------------
diff --cc usage/web-console/grails-app/conf/UrlMappings.groovy
index a194184,a194184..f8bd80b
--- a/usage/web-console/grails-app/conf/UrlMappings.groovy
+++ b/usage/web-console/grails-app/conf/UrlMappings.groovy
@@@ -17,4 -17,4 +17,14 @@@ class UrlMappings 
  
          "500"(view:'/error')
      }
++    
++    def beforeInterceptor = [action: this.&auth, except: 'login']
++    // defined with private scope, so it's not considered an action
++    private auth() {
++        if (!session.user) {
++            redirect(action: 'login')
++            return false
++        }
++    }
++    
  }

http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/d3c8046e/usage/web-console/grails-app/controllers/brooklyn/web/console/DashboardController.groovy
----------------------------------------------------------------------
diff --cc 
usage/web-console/grails-app/controllers/brooklyn/web/console/DashboardController.groovy
index 5089a00,5089a00..56bcd2c
--- 
a/usage/web-console/grails-app/controllers/brooklyn/web/console/DashboardController.groovy
+++ 
b/usage/web-console/grails-app/controllers/brooklyn/web/console/DashboardController.groovy
@@@ -8,7 -8,7 +8,7 @@@ class DashboardController 
      public static final Logger LOG = LoggerFactory.getLogger(this);
              
      def index = {
--        LOG.info("loading dashboard for "+session)
++        LOG.debug("loading dashboard for {}", session)
      }
      
  }

http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/d3c8046e/usage/web-console/grails-app/controllers/brooklyn/web/console/EntityController.groovy
----------------------------------------------------------------------
diff --cc 
usage/web-console/grails-app/controllers/brooklyn/web/console/EntityController.groovy
index d3e2709,d3e2709..ed119cc
--- 
a/usage/web-console/grails-app/controllers/brooklyn/web/console/EntityController.groovy
+++ 
b/usage/web-console/grails-app/controllers/brooklyn/web/console/EntityController.groovy
@@@ -1,7 -1,7 +1,6 @@@
  package brooklyn.web.console
  
  import grails.converters.JSON
--import grails.plugins.springsecurity.Secured
  
  import brooklyn.entity.Entity
  import brooklyn.web.console.entity.EntitySummary
@@@ -20,7 -20,7 +19,7 @@@ import brooklyn.entity.Group
  import brooklyn.entity.ParameterType
  import brooklyn.entity.basic.BasicParameterType
  
--@Secured(['ROLE_ADMIN'])
++
  class EntityController {
  
      // Injected

http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/d3c8046e/usage/web-console/grails-app/controllers/brooklyn/web/console/LoginController.groovy
----------------------------------------------------------------------
diff --cc 
usage/web-console/grails-app/controllers/brooklyn/web/console/LoginController.groovy
index 8d411fb,8d411fb..a6170a4
--- 
a/usage/web-console/grails-app/controllers/brooklyn/web/console/LoginController.groovy
+++ 
b/usage/web-console/grails-app/controllers/brooklyn/web/console/LoginController.groovy
@@@ -2,94 -2,94 +2,57 @@@ package brooklyn.web.consol
  
  import grails.converters.JSON
  
--import javax.security.auth.login.AccountExpiredException
  import javax.servlet.http.HttpServletResponse
  
--import org.codehaus.groovy.grails.plugins.springsecurity.SpringSecurityUtils
--import org.codehaus.groovy.grails.web.context.ServletContextHolder
--import org.springframework.security.authentication.CredentialsExpiredException
--import org.springframework.security.authentication.DisabledException
--import org.springframework.security.authentication.LockedException
--import 
org.springframework.security.authentication.UsernamePasswordAuthenticationToken
--import org.springframework.security.core.authority.GrantedAuthorityImpl
--import org.springframework.security.core.context.SecurityContextHolder
--import org.springframework.security.web.WebAttributes
--import 
org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter
--
--import brooklyn.config.BrooklynServiceAttributes;
++import brooklyn.web.console.security.WebConsoleSecurity
  
  class LoginController {
  
--    /**
--     * Dependency injection for the authenticationTrustResolver.
--     */
--    def authenticationTrustResolver
--
--    /**
--     * Dependency injection for the springSecurityService.
--     */
--    def springSecurityService
--
++    private String getRedirectTarget(boolean includePrefix) {
++        String result = "/dashboard/"; 
++        if (includePrefix) result = createLinkTo(dir:result)
++        return result;
++    }
++    
      /**
       * Default action; redirects to 'defaultTargetUrl' if logged in, 
/login/auth otherwise.
       */
      def index = {
--        if (springSecurityService.isLoggedIn()) {
--            redirect uri: 
SpringSecurityUtils.securityConfig.successHandler.defaultTargetUrl
--        }
--        else {
++        if (WebConsoleSecurity.getInstance().isAuthenticated(session) || 
tryAuthenticate()) {
++            redirect uri: getRedirectTarget(false);
++        } else {
              redirect action: auth, params: params
          }
      }
  
--    //logs the given user in
--    private void autoLogin(username) {
--        def user = SecurityUser.findByUsername(username)
--        List auths = user.authorities.collect {
--            new GrantedAuthorityImpl(it.authority)
++    private boolean tryAuthenticate() {
++        if (params.j_username) {
++            return WebConsoleSecurity.getInstance().authenticate(session, 
params.j_username, params.j_password)
          }
--        def grailsUser = new 
org.springframework.security.core.userdetails.User(
--            user.username, // String username, String password, 
--            "",
--            true, true, // boolean enabled, boolean accountNonExpired,
--            true, true, // boolean credentialsNonExpired, boolean 
accountNonLocked, 
--            auths //Collection<? extends GrantedAuthority> authorities);
--            );
--        def authToken =  new UsernamePasswordAuthenticationToken(grailsUser, 
'', auths)
--        SecurityContextHolder.context.authentication = authToken
++        return false;
      }
--    
      /**
       * Show the login page.
       */
      def auth = {
--        def config = SpringSecurityUtils.securityConfig
--        //ideally we'd do this on start of any new session, but not clear how 
to do it
--        //(this probably isn't the best workaround to allow tests to access 
in any case!) 
--        if (!springSecurityService.isLoggedIn()) {
--            //for unit tests
--            def autologinUser = 
ServletContextHolder.servletContext?.getAttribute(BrooklynServiceAttributes.BROOKLYN_AUTOLOGIN_USERNAME);
--            if (autologinUser) {
--                autoLogin(autologinUser)
--            }
--        }
--
--        if (springSecurityService.isLoggedIn()) {
--            redirect uri: config.successHandler.defaultTargetUrl
++        if (WebConsoleSecurity.getInstance().isAuthenticated(session)) {
++            redirect uri: getRedirectTarget(false)
              return
          }
  
          String view = 'auth'
--        String postUrl = 
"${request.contextPath}${config.apf.filterProcessesUrl}"
++        String postUrl = createLinkTo(dir:"login")
++        //TODO support following to the originally requested URL
++        String postAfterUrl = getRedirectTarget(true)
          render view: view, model: [postUrl: postUrl,
--                                   rememberMeParameter: 
config.rememberMe.parameter]
++                                   rememberMeParameter: true]
      }
  
      /**
       * The redirect action for Ajax requests. 
       */
      def authAjax = {
--        response.setHeader 'Location', 
SpringSecurityUtils.securityConfig.auth.ajaxLoginFormUrl
++//        response.setHeader 'Location', 
SpringSecurityUtils.securityConfig.auth.ajaxLoginFormUrl
          response.sendError HttpServletResponse.SC_UNAUTHORIZED
      }
  
@@@ -97,63 -97,63 +60,63 @@@
       * Show denied page.
       */
      def denied = {
--        if (springSecurityService.isLoggedIn() &&
--                
authenticationTrustResolver.isRememberMe(SCH.context?.authentication)) {
--            // have cookie but the page is guarded with IS_AUTHENTICATED_FULLY
--            redirect action: full, params: params
--        }
++//        if (springSecurityService.isLoggedIn() &&
++//                
authenticationTrustResolver.isRememberMe(SCH.context?.authentication)) {
++//            // have cookie but the page is guarded with 
IS_AUTHENTICATED_FULLY
++//            redirect action: full, params: params
++//        }
      }
  
      /**
       * Login page for users with a remember-me cookie but accessing a 
IS_AUTHENTICATED_FULLY page.
       */
      def full = {
--        def config = SpringSecurityUtils.securityConfig
--        render view: 'auth', params: params,
--            model: [hasCookie: 
authenticationTrustResolver.isRememberMe(SCH.context?.authentication),
--                    postUrl: 
"${request.contextPath}${config.apf.filterProcessesUrl}"]
++//        def config = SpringSecurityUtils.securityConfig
++//        render view: 'auth', params: params,
++//            model: [hasCookie: 
authenticationTrustResolver.isRememberMe(SCH.context?.authentication),
++//                    postUrl: 
"${request.contextPath}${config.apf.filterProcessesUrl}"]
      }
  
      /**
       * Callback after a failed login. Redirects to the auth page with a 
warning message.
       */
      def authfail = {
--
--        def username = 
session[UsernamePasswordAuthenticationFilter.SPRING_SECURITY_LAST_USERNAME_KEY]
--        String msg = ''
--        def exception = session[WebAttributes.AUTHENTICATION_EXCEPTION]
--        if (exception) {
--            if (exception instanceof AccountExpiredException) {
--                msg = SpringSecurityUtils.securityConfig.errors.login.expired
--            }
--            else if (exception instanceof CredentialsExpiredException) {
--                msg = 
SpringSecurityUtils.securityConfig.errors.login.passwordExpired
--            }
--            else if (exception instanceof DisabledException) {
--                msg = SpringSecurityUtils.securityConfig.errors.login.disabled
--            }
--            else if (exception instanceof LockedException) {
--                msg = SpringSecurityUtils.securityConfig.errors.login.locked
--            }
--            else {
--                msg = SpringSecurityUtils.securityConfig.errors.login.fail
--            }
--        }
--
--        if (springSecurityService.isAjax(request)) {
--            render([error: msg] as JSON)
--        }
--        else {
++//
++//        def username = 
session[UsernamePasswordAuthenticationFilter.SPRING_SECURITY_LAST_USERNAME_KEY]
++//        String msg = ''
++//        def exception = session[WebAttributes.AUTHENTICATION_EXCEPTION]
++//        if (exception) {
++//            if (exception instanceof AccountExpiredException) {
++//                msg = 
SpringSecurityUtils.securityConfig.errors.login.expired
++//            }
++//            else if (exception instanceof CredentialsExpiredException) {
++//                msg = 
SpringSecurityUtils.securityConfig.errors.login.passwordExpired
++//            }
++//            else if (exception instanceof DisabledException) {
++//                msg = 
SpringSecurityUtils.securityConfig.errors.login.disabled
++//            }
++//            else if (exception instanceof LockedException) {
++//                msg = SpringSecurityUtils.securityConfig.errors.login.locked
++//            }
++//            else {
++//                msg = SpringSecurityUtils.securityConfig.errors.login.fail
++//            }
++//        }
++//
++//        if (springSecurityService.isAjax(request)) {
++//            render([error: msg] as JSON)
++//        }
++//        else {
              flash.message = msg
              redirect action: auth, params: params
--        }
++//        }
      }
  
      /**
       * The Ajax success redirect url.
       */
      def ajaxSuccess = {
--        render([success: true, username: 
springSecurityService.authentication.name] as JSON)
++        render([success: true] as JSON)
      }
  
      /**

http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/d3c8046e/usage/web-console/grails-app/controllers/brooklyn/web/console/LogoutController.groovy
----------------------------------------------------------------------
diff --cc 
usage/web-console/grails-app/controllers/brooklyn/web/console/LogoutController.groovy
index 2ff87f0,2ff87f0..3b39365
--- 
a/usage/web-console/grails-app/controllers/brooklyn/web/console/LogoutController.groovy
+++ 
b/usage/web-console/grails-app/controllers/brooklyn/web/console/LogoutController.groovy
@@@ -1,6 -1,6 +1,6 @@@
  package brooklyn.web.console
  
--import org.codehaus.groovy.grails.plugins.springsecurity.SpringSecurityUtils
++import brooklyn.web.console.security.WebConsoleSecurity
  
  class LogoutController {
  
@@@ -8,7 -8,7 +8,8 @@@
       * Index action. Redirects to the Spring security logout uri.
       */
      def index = {
--        // TODO  put any pre-logout code here
--        redirect uri: 
SpringSecurityUtils.securityConfig.logout.filterProcessesUrl // 
'/j_spring_security_logout'
++        WebConsoleSecurity.getInstance().logout(session)
++        redirect controller: 'login';
      }
++    
  }

http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/d3c8046e/usage/web-console/grails-app/domain/brooklyn/web/console/SecurityRole.groovy
----------------------------------------------------------------------
diff --cc 
usage/web-console/grails-app/domain/brooklyn/web/console/SecurityRole.groovy
index 57cd47c,57cd47c..0000000
deleted file mode 100644,100644
--- 
a/usage/web-console/grails-app/domain/brooklyn/web/console/SecurityRole.groovy
+++ /dev/null
@@@ -1,14 -1,14 +1,0 @@@
--package brooklyn.web.console
--
--class SecurityRole {
--
--    String authority
--
--    static mapping = {
--        cache true
--    }
--
--    static constraints = {
--        authority blank: false, unique: true
--    }
--}

http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/d3c8046e/usage/web-console/grails-app/domain/brooklyn/web/console/SecurityUser.groovy
----------------------------------------------------------------------
diff --cc 
usage/web-console/grails-app/domain/brooklyn/web/console/SecurityUser.groovy
index 7130f04,7130f04..0000000
deleted file mode 100644,100644
--- 
a/usage/web-console/grails-app/domain/brooklyn/web/console/SecurityUser.groovy
+++ /dev/null
@@@ -1,24 -1,24 +1,0 @@@
--package brooklyn.web.console
--
--class SecurityUser {
--
--    String username
--    String password
--    boolean enabled
--    boolean accountExpired
--    boolean accountLocked
--    boolean passwordExpired
--
--    static constraints = {
--        username blank: false, unique: true
--        password blank: false
--    }
--
--    static mapping = {
--        password column: '`password`'
--    }
--
--    Set<SecurityRole> getAuthorities() {
--        SecurityUserRole.findAllBySecurityUser(this).collect { 
it.securityRole } as Set
--    }
--}

http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/d3c8046e/usage/web-console/grails-app/domain/brooklyn/web/console/SecurityUserRole.groovy
----------------------------------------------------------------------
diff --cc 
usage/web-console/grails-app/domain/brooklyn/web/console/SecurityUserRole.groovy
index b7cf771,b7cf771..0000000
deleted file mode 100644,100644
--- 
a/usage/web-console/grails-app/domain/brooklyn/web/console/SecurityUserRole.groovy
+++ /dev/null
@@@ -1,52 -1,52 +1,0 @@@
--package brooklyn.web.console
--
--import org.apache.commons.lang.builder.HashCodeBuilder
--
--class SecurityUserRole implements Serializable {
--
--    SecurityUser securityUser
--    SecurityRole securityRole
--
--    boolean equals(other) {
--        if (!(other instanceof SecurityUserRole)) {
--            return false
--        }
--
--        other.securityUser?.id == securityUser?.id &&
--            other.securityRole?.id == securityRole?.id
--    }
--
--    int hashCode() {
--        def builder = new HashCodeBuilder()
--        if (securityUser) builder.append(securityUser.id)
--        if (securityRole) builder.append(securityRole.id)
--        builder.toHashCode()
--    }
--
--    static SecurityUserRole get(long securityUserId, long securityRoleId) {
--        find 'from SecurityUserRole where securityUser.id=:securityUserId and 
securityRole.id=:securityRoleId',
--            [securityUserId: securityUserId, securityRoleId: securityRoleId]
--    }
--
--    static SecurityUserRole create(SecurityUser securityUser, SecurityRole 
securityRole, boolean flush = false) {
--        new SecurityUserRole(securityUser: securityUser, securityRole: 
securityRole).save(flush: flush, insert: true)
--    }
--
--    static boolean remove(SecurityUser securityUser, SecurityRole 
securityRole, boolean flush = false) {
--        SecurityUserRole instance = 
SecurityUserRole.findBySecurityUserAndSecurityRole(securityUser, securityRole)
--        instance ? instance.delete(flush: flush) : false
--    }
--
--    static void removeAll(SecurityUser securityUser) {
--        executeUpdate 'DELETE FROM SecurityUserRole WHERE 
securityUser=:securityUser', [securityUser: securityUser]
--    }
--
--    static void removeAll(SecurityRole securityRole) {
--        executeUpdate 'DELETE FROM SecurityUserRole WHERE 
securityRole=:securityRole', [securityRole: securityRole]
--    }
--
--    static mapping = {
--        id composite: ['securityRole', 'securityUser']
--        version false
--    }
--}

http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/d3c8046e/usage/web-console/grails-app/services/brooklyn/web/console/ManagementContextService.groovy
----------------------------------------------------------------------
diff --cc 
usage/web-console/grails-app/services/brooklyn/web/console/ManagementContextService.groovy
index bb3be3e,bb3be3e..bd1f2ca
--- 
a/usage/web-console/grails-app/services/brooklyn/web/console/ManagementContextService.groovy
+++ 
b/usage/web-console/grails-app/services/brooklyn/web/console/ManagementContextService.groovy
@@@ -11,6 -11,6 +11,8 @@@ import java.util.concurrent.atomic.Atom
  import org.codehaus.groovy.grails.web.context.ServletContextHolder
  
  class ManagementContextService {
++    static transactional = false
++    
      private ManagementContext managementContext
      protected static AtomicLong ID_GENERATOR = new AtomicLong(0L)
  
@@@ -27,7 -27,7 +29,7 @@@
              managementContext = (ManagementContext) 
ServletContextHolder.servletContext?.
                  
getAttribute(BrooklynServiceAttributes.BROOKLYN_MANAGEMENT_CONTEXT)
          }
--        // TODO remove this test code as soon as the group agrees it's 
unnecessary!
++        // TODO use a different mechanism for specifying test-app
          if (!managementContext) {
              managementContext = new LocalManagementContext();
              managementContext.manage(new TestWebApplication())

http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/d3c8046e/usage/web-console/grails-app/services/brooklyn/web/console/TestWebApplication.groovy
----------------------------------------------------------------------
diff --cc 
usage/web-console/grails-app/services/brooklyn/web/console/TestWebApplication.groovy
index db196f7,db196f7..0000000
deleted file mode 100644,100644
--- 
a/usage/web-console/grails-app/services/brooklyn/web/console/TestWebApplication.groovy
+++ /dev/null
@@@ -1,276 -1,276 +1,0 @@@
--package brooklyn.web.console
--
--import brooklyn.entity.Effector
--import brooklyn.entity.Entity
--import brooklyn.entity.ParameterType
--import brooklyn.entity.basic.AbstractApplication
--import brooklyn.entity.basic.AbstractEntity
--import brooklyn.entity.basic.AbstractGroup
--import brooklyn.entity.basic.BasicParameterType
--import brooklyn.entity.basic.EntityLocal;
--import brooklyn.entity.webapp.tomcat.TomcatServer
--import brooklyn.event.basic.BasicAttributeSensor
--import brooklyn.location.Location
--import brooklyn.location.basic.SimulatedLocation
--import brooklyn.policy.Policy
--import brooklyn.policy.basic.GeneralPurposePolicy
--import brooklyn.management.Task
--import brooklyn.web.console.entity.TestEffector
--import grails.converters.JSON
--import brooklyn.event.basic.BasicSensor
--import brooklyn.event.Sensor
--import brooklyn.event.AttributeSensor
--
--// TODO remove these test classes as soon as the group agrees they're 
unnecessary!
--private class TestWebApplication extends AbstractApplication {
--    TestWebApplication(Map props=[:]) {
--        super(props)
--        displayName = "Application";
--
--        locations = [
--            new SimulatedLocation([id: "us-east-1", name:"US-East-1", 
iso3166: "US-CA", displayName:"US-East-1", streetAddress:"Northern Virginia, 
USA", description:"Northern Virginia (approx)",
--                                        latitude:38.0,longitude:-76.0]),
--            new SimulatedLocation([id: "us-west-1", name:"US-West-1", 
iso3166: "US-VA", displayName:"US-West-1", streetAddress:"Northern California, 
USA", description:"Northern California",
--                                        latitude:40.0,longitude:-120.0]),
--            new SimulatedLocation([id: "eu-west-1", name:"EU-West-1", 
iso3166: "IE", displayName:"EU-West-1", streetAddress:"Dublin, Ireland", 
description:"Dublin, Ireland",
--                                        
latitude:53.34778,longitude:-6.25972]),
--            new SimulatedLocation([id: "fruitcake", name:"Unused location in 
cakeland", iso3166: "IE", displayName:"Unused location in cakeland", 
streetAddress:"Nowhere, cakeland", description:"Nowhere",
--                                        latitude:0,longitude:0])
--        ];
--
--        List<Policy> testPolicies = [
--            new GeneralPurposePolicy([id: 'CTS1', name: 'chase-the-sun', 
displayName: 'Chase the Sun', policyStatus: 'Suspended']),
--            new GeneralPurposePolicy([id: 'CTM1', name: 'chase-the-moon', 
displayName: 'Chase the Moon', policyStatus: 'Active']),
--            new GeneralPurposePolicy([id: 'FTM1', name: 'follow-the-money', 
displayName: 'Follow the Money', policyStatus: 'Suspended']),
--            new GeneralPurposePolicy([id: 'FTA1', name: 'follow-the-action', 
displayName: 'Follow the Action', policyStatus: 'Active'])
--        ];
--
--
--        Entity testExtraGroup = new TestGroupEntity(this, "Another group for 
testing");
--        setupChangingEntity(this);
--        
--        for(String tierName : ["tomcat tier 1", "tomcat tier 2", "data tier 
1"]) {
--            Entity tier = new TestGroupEntity(this, tierName);
--            for(String clusterName : ["1a", "1b"]) {
--                Entity cluster = new TestGroupEntity(tier, 
tierName.substring(0, tierName.indexOf(" ")) +
--                        " cluster " + clusterName)
--                for(int i=1; i<4; i++) {
--                    if (tierName =~ /^tomcat/) {
--                        Entity testTomcat = new TestTomcatEntity(cluster, 
"tomcat node " + clusterName + "." + i)
--                        testTomcat.addGroup(testExtraGroup);
--                        cluster.addOwnedChild(testTomcat)
--                        setUpAddingSensor(testTomcat)
--                    } else {
--                        cluster.addOwnedChild(new TestDataEntity(cluster, 
"data node " + clusterName + "." + i))
--                    }
--
--                }
--                tier.addOwnedChild(cluster)
--            }
--            addOwnedChild(tier)
--        }
--
--        sensors.putAll([
--                Children: new BasicAttributeSensor<Integer>(Integer.class, 
"Children",
--                        "Owned children of this application"),
--                        DataRate: new 
BasicAttributeSensor<String>(String.class, "DataRate")])
--        setAttribute(getSensor("Children"), getOwnedChildren().size())
--    }
--
--    public <T> Task<T> invoke(Effector<T> eff, Map<String, ?> parameters) {
--        return null  //To change body of implemented methods use File | 
Settings | File Templates.
--    }
--
--    /**
--     * Sets up an entity that is added and removed every 20s.
--     * @param application
--     */
--    private void setupChangingEntity(final AbstractApplication application) {
--        Runnable r = new Runnable() {
--            Entity e;
--            void run() {
--                while (true) {
--                    if (e != null) {
--                        application.removeOwnedChild(e)
--                        e = null;
--                        Thread.sleep(20*1000L);
--                    } else {
--                        e = new TestGroupEntity(application, "Now you see 
me");
--                        Thread.sleep(20*1000L);
--                    }
--                }
--            }
--
--        };
--
--        new Thread(r).start();
--    }
--
--    private void setUpAddingSensor(AbstractEntity entity) {
--        Runnable r = new Runnable() {
--            void run() {
--                while (true) {
--                    Sensor sensor = new BasicAttributeSensor(Sensor.class, 
"test.sensor", "Added and removed every 20s")
--                    entity.addSensor(sensor)
--                    Thread.sleep(20*1000L)
--                    entity.removeSensor(sensor.name)
--                    Thread.sleep(20*1000L)
--                }
--            }
--
--        };
--        new Thread(r).start();
--    }
--
--    private class TestGroupEntity extends AbstractGroup {
--        TestGroupEntity(Entity owner, String displayName) {
--            super([:], owner)
--            this.displayName = displayName
--            sensors.putAll([Children: new 
BasicAttributeSensor<Integer>(Integer.class, "Children",
--                    "Direct children of this group"), DataRate: new 
BasicAttributeSensor<String>(String.class, "DataRate")])
--        }
--
--        TestGroupEntity addOwnedChild(Entity child) {
--            super.addOwnedChild(child)
--            setAttribute(getSensor("Children"), ownedChildren.size())
--            return this
--        }
--
--        public <T> Task<T> invoke(Effector<T> eff, Map<String, ?> parameters) 
{
--            return null
--        }
--    }
--
--
--    private class TestDataEntity extends AbstractEntity {
--        private List<Location> testLocations = [
--            new SimulatedLocation([id: "us-east-1", name:"US-East-1", 
iso3166: "US-CA", displayName:"US-East-1", streetAddress:"Northern Virginia, 
USA", description:"Northern Virginia (approx)",
--                                        latitude:38.0,longitude:-76.0]),
--            new SimulatedLocation([id: "us-west-1", name:"US-West-1", 
iso3166: "US-VA", displayName:"US-West-1", streetAddress:"Northern California, 
USA", description:"Northern California",
--                                        latitude:40.0,longitude:-120.0]),
--            new SimulatedLocation([id: "eu-west-1", name:"EU-West-1", 
iso3166: "IE", displayName:"EU-West-1", streetAddress:"Dublin, Ireland", 
description:"Dublin, Ireland",
--                                        latitude:53.34778,longitude:-6.25972])
--        ];
--        private List<Policy> testPolicies = [
--            new GeneralPurposePolicy([id: 'CTS1', name: 'chase-the-sun', 
displayName: 'Chase the Sun', policyStatus: 'Suspended', description: 'Chasing 
the sun, meaning chase the activity when certain parts of the earth are 
awake']),
--            new GeneralPurposePolicy([id: 'CTM1', name: 'chase-the-moon', 
displayName: 'Chase the Moon', policyStatus: 'Active'])
--        ];
--        TestDataEntity(Entity owner, String displayName) {
--            super([:], owner)
--
--            this.displayName = displayName
--            this.locations = testLocations;
--            this.policies = testPolicies;
--            TestEffector startDB = new TestEffector("Start DB", "This will 
start the database",
--                    new ArrayList<ParameterType<?>>())
--            TestEffector stopDB = new TestEffector("Stop DB", "This will stop 
the database",
--                    new ArrayList<ParameterType<?>>())
--            TestEffector restartDB = new TestEffector("Restart DB", "This 
will restart the DB",
--                    new ArrayList<ParameterType<?>>())
--
--            this.effectors.putAll(["Start DB": startDB, "Stop DB": stopDB, 
"Restart DB": restartDB])
--
--            this.sensors.putAll(
--                   [Happiness: new BasicAttributeSensor<String>(String.class, 
"Happiness"),
--                    Cache: new BasicAttributeSensor<String>(String.class, 
"Cache", "Some cache metric"),
--                    Sync: new BasicAttributeSensor<String>(String.class, 
"Sync", "Synchronization strategy")]
--            )
--
--            setAttribute(getSensor("Happiness"), 50)
--            setAttribute(getSensor("Cache"), 200)
--            setAttribute(getSensor("Sync"), "Moop")
--        }
--
--        public <T> Task<T> invoke(Effector<T> eff, Map<String, ?> parameters) 
{
--            return null
--        }
--    }
--
--    private class TestTomcatEntity extends AbstractEntity {
--        //FIXME should use typed keys not strings
--        private Map hackMeIn = [
--                "http.port": 8080,
--                "webapp.tomcat.shutdownPort": 666,
--                "jmx.port": 1000,
--                "webapp.reqs.processing.time": 100,
--                "test.sensor": 17
--        ]
--
--        private List<Location> testLocations = [
--                new SimulatedLocation([id: "us-east-1", name:"US-East-1", 
iso3166: "US-VA", displayName:"US-East-1", streetAddress:"Northern Virginia, 
USA", description:"Northern Virginia (approx)",
--                                            latitude:38.0,longitude:-76.0])
--        ];
--        private List<Policy> testPolicies = [
--            new GeneralPurposePolicy([id: 'FTM1', name: 'follow-the-money', 
displayName: 'Follow the Money', policyStatus: 'Suspended']),
--            new GeneralPurposePolicy([id: 'FTA1', name: 'follow-the-action', 
displayName: 'Follow the Action', policyStatus: 'Active'])
--        ];
--
--
--        public TestTomcatEntity(Entity owner, String displayName) {
--            super([:], owner)
--            this.displayName = displayName
--            this.locations = testLocations;
--            this.policies = testPolicies;
--
--            // Stealing the sensors from TomcatNode
--            this.sensors.putAll(new TomcatServer().sensors)
--
--            List<ParameterType<?>> parameterTypeList = new 
ArrayList<ParameterType<?>>()
--            ParameterType tomcatStartLocation = new 
BasicParameterType("Location", new ArrayList<String>().class)
--            ParameterType actionDate = new BasicParameterType("Date", 
Date.class)
--            parameterTypeList.add(tomcatStartLocation)
--            parameterTypeList.add(actionDate)
--
--
--            // Don't appear to be any effectors in TomcatServer
--            TestEffector startTomcat = new TestEffector("Start Tomcat",
--                                                        "This will start 
Tomcat at a specified location",
--                                                        parameterTypeList)
--            TestEffector stopTomcat = new TestEffector("Stop Tomcat",
--                                                        "This will stop 
tomcat at its current location",
--                                                        new 
Collections.SingletonList(actionDate))
--            TestEffector restartTomcat = new TestEffector("Restart Tomcat",
--                                                          "This will restart 
tomcat in its current location",
--                                                          new 
ArrayList<ParameterType<?>>())
--
--            this.effectors.putAll([  "Start Tomcat": startTomcat,
--                                "Stop Tomcat": stopTomcat,
--                                "Restart Tomcat": restartTomcat])
--
--            for (def i = 0; i < 10; ++i) {
--                this.getExecutionContext().submit([
--                                                      tags:["EFFECTOR"],
--                                                      tag:this,
--                                                      displayName: "Update 
values (test " + i + ")",
--                                                      description: "This 
updates sensor values"],
--                                                  new MyRunnable(this));
--            }
--        }
--
--        public <T> Task<T> invoke(Effector<T> eff, Map<String, ?> parameters) 
{
--            System.out.println(parameters as JSON)
--            return null
--        }
--
--        protected class MyRunnable implements Runnable {
--            EntityLocal entity
--            protected MyRunnable(Entity e) {
--                this.entity = e
--            }
--            void run() {
--                while (true) {
--                    Map ss = entity.getSensors()
--                    for (String key: hackMeIn.keySet()) {
--                        def s = ss[key]
--                        if (s != null){
--                            entity.setAttribute(s,
--                                hackMeIn[key] + 
ManagementContextService.ID_GENERATOR +
--                                    ((int) 1000 * Math.random()))
--                        }
--                    }
--                    Thread.sleep(5000)
--                }
--            }
--        }
--    }
--}

http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/d3c8046e/usage/web-console/grails-app/utils/brooklyn/web/console/TestWebApplication.groovy
----------------------------------------------------------------------
diff --cc 
usage/web-console/grails-app/utils/brooklyn/web/console/TestWebApplication.groovy
index 0000000,0000000..db196f7
new file mode 100644
--- /dev/null
+++ 
b/usage/web-console/grails-app/utils/brooklyn/web/console/TestWebApplication.groovy
@@@ -1,0 -1,0 +1,276 @@@
++package brooklyn.web.console
++
++import brooklyn.entity.Effector
++import brooklyn.entity.Entity
++import brooklyn.entity.ParameterType
++import brooklyn.entity.basic.AbstractApplication
++import brooklyn.entity.basic.AbstractEntity
++import brooklyn.entity.basic.AbstractGroup
++import brooklyn.entity.basic.BasicParameterType
++import brooklyn.entity.basic.EntityLocal;
++import brooklyn.entity.webapp.tomcat.TomcatServer
++import brooklyn.event.basic.BasicAttributeSensor
++import brooklyn.location.Location
++import brooklyn.location.basic.SimulatedLocation
++import brooklyn.policy.Policy
++import brooklyn.policy.basic.GeneralPurposePolicy
++import brooklyn.management.Task
++import brooklyn.web.console.entity.TestEffector
++import grails.converters.JSON
++import brooklyn.event.basic.BasicSensor
++import brooklyn.event.Sensor
++import brooklyn.event.AttributeSensor
++
++// TODO remove these test classes as soon as the group agrees they're 
unnecessary!
++private class TestWebApplication extends AbstractApplication {
++    TestWebApplication(Map props=[:]) {
++        super(props)
++        displayName = "Application";
++
++        locations = [
++            new SimulatedLocation([id: "us-east-1", name:"US-East-1", 
iso3166: "US-CA", displayName:"US-East-1", streetAddress:"Northern Virginia, 
USA", description:"Northern Virginia (approx)",
++                                        latitude:38.0,longitude:-76.0]),
++            new SimulatedLocation([id: "us-west-1", name:"US-West-1", 
iso3166: "US-VA", displayName:"US-West-1", streetAddress:"Northern California, 
USA", description:"Northern California",
++                                        latitude:40.0,longitude:-120.0]),
++            new SimulatedLocation([id: "eu-west-1", name:"EU-West-1", 
iso3166: "IE", displayName:"EU-West-1", streetAddress:"Dublin, Ireland", 
description:"Dublin, Ireland",
++                                        
latitude:53.34778,longitude:-6.25972]),
++            new SimulatedLocation([id: "fruitcake", name:"Unused location in 
cakeland", iso3166: "IE", displayName:"Unused location in cakeland", 
streetAddress:"Nowhere, cakeland", description:"Nowhere",
++                                        latitude:0,longitude:0])
++        ];
++
++        List<Policy> testPolicies = [
++            new GeneralPurposePolicy([id: 'CTS1', name: 'chase-the-sun', 
displayName: 'Chase the Sun', policyStatus: 'Suspended']),
++            new GeneralPurposePolicy([id: 'CTM1', name: 'chase-the-moon', 
displayName: 'Chase the Moon', policyStatus: 'Active']),
++            new GeneralPurposePolicy([id: 'FTM1', name: 'follow-the-money', 
displayName: 'Follow the Money', policyStatus: 'Suspended']),
++            new GeneralPurposePolicy([id: 'FTA1', name: 'follow-the-action', 
displayName: 'Follow the Action', policyStatus: 'Active'])
++        ];
++
++
++        Entity testExtraGroup = new TestGroupEntity(this, "Another group for 
testing");
++        setupChangingEntity(this);
++        
++        for(String tierName : ["tomcat tier 1", "tomcat tier 2", "data tier 
1"]) {
++            Entity tier = new TestGroupEntity(this, tierName);
++            for(String clusterName : ["1a", "1b"]) {
++                Entity cluster = new TestGroupEntity(tier, 
tierName.substring(0, tierName.indexOf(" ")) +
++                        " cluster " + clusterName)
++                for(int i=1; i<4; i++) {
++                    if (tierName =~ /^tomcat/) {
++                        Entity testTomcat = new TestTomcatEntity(cluster, 
"tomcat node " + clusterName + "." + i)
++                        testTomcat.addGroup(testExtraGroup);
++                        cluster.addOwnedChild(testTomcat)
++                        setUpAddingSensor(testTomcat)
++                    } else {
++                        cluster.addOwnedChild(new TestDataEntity(cluster, 
"data node " + clusterName + "." + i))
++                    }
++
++                }
++                tier.addOwnedChild(cluster)
++            }
++            addOwnedChild(tier)
++        }
++
++        sensors.putAll([
++                Children: new BasicAttributeSensor<Integer>(Integer.class, 
"Children",
++                        "Owned children of this application"),
++                        DataRate: new 
BasicAttributeSensor<String>(String.class, "DataRate")])
++        setAttribute(getSensor("Children"), getOwnedChildren().size())
++    }
++
++    public <T> Task<T> invoke(Effector<T> eff, Map<String, ?> parameters) {
++        return null  //To change body of implemented methods use File | 
Settings | File Templates.
++    }
++
++    /**
++     * Sets up an entity that is added and removed every 20s.
++     * @param application
++     */
++    private void setupChangingEntity(final AbstractApplication application) {
++        Runnable r = new Runnable() {
++            Entity e;
++            void run() {
++                while (true) {
++                    if (e != null) {
++                        application.removeOwnedChild(e)
++                        e = null;
++                        Thread.sleep(20*1000L);
++                    } else {
++                        e = new TestGroupEntity(application, "Now you see 
me");
++                        Thread.sleep(20*1000L);
++                    }
++                }
++            }
++
++        };
++
++        new Thread(r).start();
++    }
++
++    private void setUpAddingSensor(AbstractEntity entity) {
++        Runnable r = new Runnable() {
++            void run() {
++                while (true) {
++                    Sensor sensor = new BasicAttributeSensor(Sensor.class, 
"test.sensor", "Added and removed every 20s")
++                    entity.addSensor(sensor)
++                    Thread.sleep(20*1000L)
++                    entity.removeSensor(sensor.name)
++                    Thread.sleep(20*1000L)
++                }
++            }
++
++        };
++        new Thread(r).start();
++    }
++
++    private class TestGroupEntity extends AbstractGroup {
++        TestGroupEntity(Entity owner, String displayName) {
++            super([:], owner)
++            this.displayName = displayName
++            sensors.putAll([Children: new 
BasicAttributeSensor<Integer>(Integer.class, "Children",
++                    "Direct children of this group"), DataRate: new 
BasicAttributeSensor<String>(String.class, "DataRate")])
++        }
++
++        TestGroupEntity addOwnedChild(Entity child) {
++            super.addOwnedChild(child)
++            setAttribute(getSensor("Children"), ownedChildren.size())
++            return this
++        }
++
++        public <T> Task<T> invoke(Effector<T> eff, Map<String, ?> parameters) 
{
++            return null
++        }
++    }
++
++
++    private class TestDataEntity extends AbstractEntity {
++        private List<Location> testLocations = [
++            new SimulatedLocation([id: "us-east-1", name:"US-East-1", 
iso3166: "US-CA", displayName:"US-East-1", streetAddress:"Northern Virginia, 
USA", description:"Northern Virginia (approx)",
++                                        latitude:38.0,longitude:-76.0]),
++            new SimulatedLocation([id: "us-west-1", name:"US-West-1", 
iso3166: "US-VA", displayName:"US-West-1", streetAddress:"Northern California, 
USA", description:"Northern California",
++                                        latitude:40.0,longitude:-120.0]),
++            new SimulatedLocation([id: "eu-west-1", name:"EU-West-1", 
iso3166: "IE", displayName:"EU-West-1", streetAddress:"Dublin, Ireland", 
description:"Dublin, Ireland",
++                                        latitude:53.34778,longitude:-6.25972])
++        ];
++        private List<Policy> testPolicies = [
++            new GeneralPurposePolicy([id: 'CTS1', name: 'chase-the-sun', 
displayName: 'Chase the Sun', policyStatus: 'Suspended', description: 'Chasing 
the sun, meaning chase the activity when certain parts of the earth are 
awake']),
++            new GeneralPurposePolicy([id: 'CTM1', name: 'chase-the-moon', 
displayName: 'Chase the Moon', policyStatus: 'Active'])
++        ];
++        TestDataEntity(Entity owner, String displayName) {
++            super([:], owner)
++
++            this.displayName = displayName
++            this.locations = testLocations;
++            this.policies = testPolicies;
++            TestEffector startDB = new TestEffector("Start DB", "This will 
start the database",
++                    new ArrayList<ParameterType<?>>())
++            TestEffector stopDB = new TestEffector("Stop DB", "This will stop 
the database",
++                    new ArrayList<ParameterType<?>>())
++            TestEffector restartDB = new TestEffector("Restart DB", "This 
will restart the DB",
++                    new ArrayList<ParameterType<?>>())
++
++            this.effectors.putAll(["Start DB": startDB, "Stop DB": stopDB, 
"Restart DB": restartDB])
++
++            this.sensors.putAll(
++                   [Happiness: new BasicAttributeSensor<String>(String.class, 
"Happiness"),
++                    Cache: new BasicAttributeSensor<String>(String.class, 
"Cache", "Some cache metric"),
++                    Sync: new BasicAttributeSensor<String>(String.class, 
"Sync", "Synchronization strategy")]
++            )
++
++            setAttribute(getSensor("Happiness"), 50)
++            setAttribute(getSensor("Cache"), 200)
++            setAttribute(getSensor("Sync"), "Moop")
++        }
++
++        public <T> Task<T> invoke(Effector<T> eff, Map<String, ?> parameters) 
{
++            return null
++        }
++    }
++
++    private class TestTomcatEntity extends AbstractEntity {
++        //FIXME should use typed keys not strings
++        private Map hackMeIn = [
++                "http.port": 8080,
++                "webapp.tomcat.shutdownPort": 666,
++                "jmx.port": 1000,
++                "webapp.reqs.processing.time": 100,
++                "test.sensor": 17
++        ]
++
++        private List<Location> testLocations = [
++                new SimulatedLocation([id: "us-east-1", name:"US-East-1", 
iso3166: "US-VA", displayName:"US-East-1", streetAddress:"Northern Virginia, 
USA", description:"Northern Virginia (approx)",
++                                            latitude:38.0,longitude:-76.0])
++        ];
++        private List<Policy> testPolicies = [
++            new GeneralPurposePolicy([id: 'FTM1', name: 'follow-the-money', 
displayName: 'Follow the Money', policyStatus: 'Suspended']),
++            new GeneralPurposePolicy([id: 'FTA1', name: 'follow-the-action', 
displayName: 'Follow the Action', policyStatus: 'Active'])
++        ];
++
++
++        public TestTomcatEntity(Entity owner, String displayName) {
++            super([:], owner)
++            this.displayName = displayName
++            this.locations = testLocations;
++            this.policies = testPolicies;
++
++            // Stealing the sensors from TomcatNode
++            this.sensors.putAll(new TomcatServer().sensors)
++
++            List<ParameterType<?>> parameterTypeList = new 
ArrayList<ParameterType<?>>()
++            ParameterType tomcatStartLocation = new 
BasicParameterType("Location", new ArrayList<String>().class)
++            ParameterType actionDate = new BasicParameterType("Date", 
Date.class)
++            parameterTypeList.add(tomcatStartLocation)
++            parameterTypeList.add(actionDate)
++
++
++            // Don't appear to be any effectors in TomcatServer
++            TestEffector startTomcat = new TestEffector("Start Tomcat",
++                                                        "This will start 
Tomcat at a specified location",
++                                                        parameterTypeList)
++            TestEffector stopTomcat = new TestEffector("Stop Tomcat",
++                                                        "This will stop 
tomcat at its current location",
++                                                        new 
Collections.SingletonList(actionDate))
++            TestEffector restartTomcat = new TestEffector("Restart Tomcat",
++                                                          "This will restart 
tomcat in its current location",
++                                                          new 
ArrayList<ParameterType<?>>())
++
++            this.effectors.putAll([  "Start Tomcat": startTomcat,
++                                "Stop Tomcat": stopTomcat,
++                                "Restart Tomcat": restartTomcat])
++
++            for (def i = 0; i < 10; ++i) {
++                this.getExecutionContext().submit([
++                                                      tags:["EFFECTOR"],
++                                                      tag:this,
++                                                      displayName: "Update 
values (test " + i + ")",
++                                                      description: "This 
updates sensor values"],
++                                                  new MyRunnable(this));
++            }
++        }
++
++        public <T> Task<T> invoke(Effector<T> eff, Map<String, ?> parameters) 
{
++            System.out.println(parameters as JSON)
++            return null
++        }
++
++        protected class MyRunnable implements Runnable {
++            EntityLocal entity
++            protected MyRunnable(Entity e) {
++                this.entity = e
++            }
++            void run() {
++                while (true) {
++                    Map ss = entity.getSensors()
++                    for (String key: hackMeIn.keySet()) {
++                        def s = ss[key]
++                        if (s != null){
++                            entity.setAttribute(s,
++                                hackMeIn[key] + 
ManagementContextService.ID_GENERATOR +
++                                    ((int) 1000 * Math.random()))
++                        }
++                    }
++                    Thread.sleep(5000)
++                }
++            }
++        }
++    }
++}

http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/d3c8046e/usage/web-console/grails-app/utils/brooklyn/web/console/security/AnyoneSecurityProvider.java
----------------------------------------------------------------------
diff --cc 
usage/web-console/grails-app/utils/brooklyn/web/console/security/AnyoneSecurityProvider.java
index 0000000,0000000..ae5685d
new file mode 100644
--- /dev/null
+++ 
b/usage/web-console/grails-app/utils/brooklyn/web/console/security/AnyoneSecurityProvider.java
@@@ -1,0 -1,0 +1,23 @@@
++package brooklyn.web.console.security;
++
++import javax.servlet.http.HttpSession;
++
++/** provider who allows everyone */
++public class AnyoneSecurityProvider implements SecurityProvider {
++
++    @Override
++    public boolean isAuthenticated(HttpSession session) {
++        return true;
++    }
++
++    @Override
++    public boolean authenticate(HttpSession session, String user, String 
password) {
++        return true;
++    }
++
++    @Override
++    public boolean logout(HttpSession session) { 
++        return true;
++    }
++    
++}

http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/d3c8046e/usage/web-console/grails-app/utils/brooklyn/web/console/security/BlackholeSecurityProvider.java
----------------------------------------------------------------------
diff --cc 
usage/web-console/grails-app/utils/brooklyn/web/console/security/BlackholeSecurityProvider.java
index 0000000,0000000..6b46e99
new file mode 100644
--- /dev/null
+++ 
b/usage/web-console/grails-app/utils/brooklyn/web/console/security/BlackholeSecurityProvider.java
@@@ -1,0 -1,0 +1,23 @@@
++package brooklyn.web.console.security;
++
++import javax.servlet.http.HttpSession;
++
++/** provider who disallows everyone */
++public class BlackholeSecurityProvider implements SecurityProvider {
++
++    @Override
++    public boolean isAuthenticated(HttpSession session) {
++        return false;
++    }
++
++    @Override
++    public boolean authenticate(HttpSession session, String user, String 
password) {
++        return false;
++    }
++
++    @Override
++    public boolean logout(HttpSession session) { 
++        return true;
++    }
++
++}

http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/d3c8046e/usage/web-console/grails-app/utils/brooklyn/web/console/security/ConfigLoader.java
----------------------------------------------------------------------
diff --cc 
usage/web-console/grails-app/utils/brooklyn/web/console/security/ConfigLoader.java
index 0000000,0000000..e103cf1
new file mode 100644
--- /dev/null
+++ 
b/usage/web-console/grails-app/utils/brooklyn/web/console/security/ConfigLoader.java
@@@ -1,0 -1,0 +1,36 @@@
++package brooklyn.web.console.security;
++
++import java.util.Enumeration;
++
++import org.codehaus.groovy.grails.web.context.ServletContextHolder;
++
++import brooklyn.config.BrooklynProperties;
++import brooklyn.util.internal.BrooklynSystemProperties;
++
++/** convenience class for accessing system properties,
++ * currently reading from brooklyn.properties
++ * but ideally reading from the management context
++ * <p>
++ * see {@link BrooklynSystemProperties} for list of keys
++ * (those starting brooklyn.security are relevant)
++ */
++public class ConfigLoader {
++
++    static BrooklynProperties _props;
++
++    static synchronized BrooklynProperties getProps() {
++        if (_props!=null) return _props;
++        _props = BrooklynProperties.Factory.newWithSystemAndEnvironment();
++        
++        Enumeration ae = 
ServletContextHolder.getServletContext().getAttributeNames();
++        while (ae.hasMoreElements()) {
++            String k = (String)ae.nextElement();
++            Object v = 
ServletContextHolder.getServletContext().getAttribute(k);
++            _props.put(k, v);
++        }
++        return _props;
++    }
++    
++    public static Object getConfig(String key) { return 
getProps().getFirst(key); }
++
++}

http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/d3c8046e/usage/web-console/grails-app/utils/brooklyn/web/console/security/DelegatingSecurityProvider.java
----------------------------------------------------------------------
diff --cc 
usage/web-console/grails-app/utils/brooklyn/web/console/security/DelegatingSecurityProvider.java
index 0000000,0000000..23ae10e
new file mode 100644
--- /dev/null
+++ 
b/usage/web-console/grails-app/utils/brooklyn/web/console/security/DelegatingSecurityProvider.java
@@@ -1,0 -1,0 +1,49 @@@
++package brooklyn.web.console.security;
++
++import javax.servlet.http.HttpSession;
++
++import org.slf4j.Logger;
++import org.slf4j.LoggerFactory;
++
++import brooklyn.util.internal.BrooklynSystemProperties;
++
++public class DelegatingSecurityProvider implements SecurityProvider {
++
++    public static final Logger log = 
LoggerFactory.getLogger(DelegatingSecurityProvider.class);
++    
++    private SecurityProvider targetProvider;
++    
++    public synchronized SecurityProvider getTargetProvider() {
++        if (this.targetProvider!=null) return targetProvider;
++        Object className = 
ConfigLoader.getConfig(BrooklynSystemProperties.SECURITY_PROVIDER.getPropertyName());
++        if (className==null) {
++            className = 
ExplicitUsersSecurityProvider.class.getCanonicalName();
++            log.info("Web console using default security provider: 
"+className);
++        } else {
++            log.info("Web console using specified security provider: 
"+className);
++        }
++        try {
++            targetProvider = (SecurityProvider) 
Class.forName(""+className).newInstance();
++        } catch (Exception e) {
++            log.warn("Web console unable to instantiate security provider 
"+className+"; all logins are being disallowed");
++            targetProvider = new BlackholeSecurityProvider();
++        }
++        return targetProvider;
++    }
++    
++    @Override
++    public boolean isAuthenticated(HttpSession session) {
++        return getTargetProvider().isAuthenticated(session);
++    }
++
++    @Override
++    public boolean authenticate(HttpSession session, String user, String 
password) {
++        return getTargetProvider().authenticate(session, user, password);
++    }
++
++    @Override
++    public boolean logout(HttpSession session) { 
++        return getTargetProvider().logout(session);
++    }
++
++}

http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/d3c8046e/usage/web-console/grails-app/utils/brooklyn/web/console/security/ExplicitUsersSecurityProvider.java
----------------------------------------------------------------------
diff --cc 
usage/web-console/grails-app/utils/brooklyn/web/console/security/ExplicitUsersSecurityProvider.java
index 0000000,0000000..a4da6ca
new file mode 100644
--- /dev/null
+++ 
b/usage/web-console/grails-app/utils/brooklyn/web/console/security/ExplicitUsersSecurityProvider.java
@@@ -1,0 -1,0 +1,98 @@@
++package brooklyn.web.console.security;
++
++import java.util.LinkedHashSet;
++import java.util.Set;
++import java.util.StringTokenizer;
++
++import javax.servlet.http.HttpSession;
++
++import org.codehaus.groovy.grails.web.context.ServletContextHolder;
++import org.slf4j.Logger;
++import org.slf4j.LoggerFactory;
++
++import brooklyn.config.BrooklynServiceAttributes;
++import brooklyn.util.internal.BrooklynSystemProperties;
++
++public class ExplicitUsersSecurityProvider implements SecurityProvider {
++
++    public static final Logger LOG = 
LoggerFactory.getLogger(ExplicitUsersSecurityProvider.class);
++    
++    public static final String AUTHENTICATION_KEY = 
ExplicitUsersSecurityProvider.class.getCanonicalName()+"."+"AUTHENTICATED";
++    
++    @Override
++    public boolean isAuthenticated(HttpSession session) {
++        if (session==null) return false;
++        if (allowAnyUser) return true;
++        Object value = session.getAttribute(AUTHENTICATION_KEY);
++        return (value!=null);
++    }
++
++    private boolean allowAnyUserWithValidPass = false;
++    private boolean allowDefaultUsers = false;
++    private boolean allowAnyUser = false;
++    
++    private Set<String> allowedUsers = null;
++    private synchronized void initialize() {
++        if (allowedUsers!=null) return;
++        allowedUsers = new LinkedHashSet<String>();
++        Object users = 
ConfigLoader.getConfig(BrooklynSystemProperties.SECURITY_PROVIDER_EXPLICIT__USERS.getPropertyName());
++        if (users==null) {
++            allowDefaultUsers = true;
++        } else if ("*".equals(users)) {
++            allowAnyUserWithValidPass = true;
++        } else {
++            StringTokenizer t = new StringTokenizer(""+users, ",");
++            while (t.hasMoreElements()) {
++                allowedUsers.add((""+t.nextElement()).trim());
++            }
++        }       
++
++        if 
(ServletContextHolder.getServletContext().getAttribute(BrooklynServiceAttributes.BROOKLYN_AUTOLOGIN_USERNAME)!=null)
 {
++            LOG.warn("Use of legacy AUTOLOGIN; replace with setting 
BrooklynSystemProperties.SECURITY_PROVIDER to 
"+AnyoneSecurityProvider.class.getCanonicalName());
++            allowAnyUser = true;
++        }
++    }
++    
++    @Override
++    public boolean authenticate(HttpSession session, String user, String 
password) {
++        if (session==null) return false;
++        if (allowAnyUser) return true;
++        initialize();
++        if (!allowAnyUserWithValidPass) {
++            if (allowDefaultUsers) {
++                if (user.equals("admin") && password.equals("password")) {
++                    return allow(session, user);
++                }
++            } 
++            if (!allowedUsers.contains(user)) {
++                LOG.info("Web console rejecting unknown user "+user);
++                return false;                
++            }
++        }
++        Object actualP = 
ConfigLoader.getConfig(BrooklynSystemProperties.SECURITY_PROVIDER_EXPLICIT__PASSWORD(user).getPropertyName());
++        if (actualP==null) {
++            LOG.info("Web console rejecting passwordless user "+user);
++            return false;
++        } else if (!actualP.equals(password)){
++            LOG.info("Web console rejecting bad password for user "+user);
++            return false;
++        } else {
++            //password is good
++            return allow(session, user);
++        }
++    }
++
++    private boolean allow(HttpSession session, String user) {
++        LOG.info("Web console "+getClass().getSimpleName()+" authenticated 
user "+user);
++        session.setAttribute(AUTHENTICATION_KEY, user);
++        return true;
++    }
++
++    @Override
++    public boolean logout(HttpSession session) { 
++        if (session==null) return false;
++        session.removeAttribute(AUTHENTICATION_KEY);
++        return true;
++    }
++
++}

http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/d3c8046e/usage/web-console/grails-app/utils/brooklyn/web/console/security/SecurityProvider.java
----------------------------------------------------------------------
diff --cc 
usage/web-console/grails-app/utils/brooklyn/web/console/security/SecurityProvider.java
index 0000000,0000000..9dcfaf6
new file mode 100644
--- /dev/null
+++ 
b/usage/web-console/grails-app/utils/brooklyn/web/console/security/SecurityProvider.java
@@@ -1,0 -1,0 +1,12 @@@
++package brooklyn.web.console.security;
++
++import javax.servlet.http.HttpSession;
++
++public interface SecurityProvider {
++
++    public boolean isAuthenticated(HttpSession session);
++    
++    public boolean authenticate(HttpSession session, String user, String 
password);
++    
++    public boolean logout(HttpSession session);
++}

http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/d3c8046e/usage/web-console/grails-app/utils/brooklyn/web/console/security/WebConsoleSecurity.java
----------------------------------------------------------------------
diff --cc 
usage/web-console/grails-app/utils/brooklyn/web/console/security/WebConsoleSecurity.java
index 0000000,0000000..185ddc4
new file mode 100644
--- /dev/null
+++ 
b/usage/web-console/grails-app/utils/brooklyn/web/console/security/WebConsoleSecurity.java
@@@ -1,0 -1,0 +1,12 @@@
++package brooklyn.web.console.security;
++
++public class WebConsoleSecurity {
++
++    static SecurityProvider instance;
++    
++    public static synchronized SecurityProvider getInstance() {
++        if (instance==null) instance = new DelegatingSecurityProvider();
++        return instance;
++    }
++    
++}

http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/d3c8046e/usage/web-console/grails-app/views/login/auth.gsp
----------------------------------------------------------------------
diff --cc usage/web-console/grails-app/views/login/auth.gsp
index 854d606,854d606..5690413
--- a/usage/web-console/grails-app/views/login/auth.gsp
+++ b/usage/web-console/grails-app/views/login/auth.gsp
@@@ -48,7 -48,7 +48,7 @@@
                        <g:if test='${flash.message}'>
                        <div class='login_message'>${flash.message}</div>
                        </g:if>
--                      <div class='fheader'>Please Login..</div>
++                      <div class='fheader'>Please Login</div>
                        <form action='${postUrl}' method='POST' id='loginForm' 
class='cssform' autocomplete='off'>
                                <p>
                                        <label for='username'>Login ID</label>

http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/d3c8046e/usage/web-console/pom.xml
----------------------------------------------------------------------
diff --cc usage/web-console/pom.xml
index 5639933,5639933..d3bb766
--- a/usage/web-console/pom.xml
+++ b/usage/web-console/pom.xml
@@@ -241,13 -241,13 +241,12 @@@
              <version>1.1.2</version>
          </dependency>
  
--        <!-- Grails defaults to Ehache for the second-level Hibernate cache. 
-->
++<!-- don't user hibernate
          <dependency>
              <groupId>org.hibernate</groupId>
              <artifactId>hibernate-ehcache</artifactId>
              <version>3.3.1.GA</version>
              <exclusions>
--                <!-- We are pulling in ehcache-core below -->
                  <exclusion>
                      <groupId>net.sf.ehcache</groupId>
                      <artifactId>ehcache</artifactId>
@@@ -261,13 -261,13 +260,12 @@@
              <version>1.7.1</version>
          </dependency>
  
--        <!-- For ease of development and testing, we include the HSQLDB 
database. -->
++-->
          <dependency>
              <groupId>hsqldb</groupId>
              <artifactId>hsqldb</artifactId>
              <version>1.8.0.10</version>
          </dependency>
--
          <dependency>
              <groupId>org.slf4j</groupId>
              <artifactId>slf4j-api</artifactId>

Reply via email to