diff --git a/plugin-core/docs/src/docs/introduction/gettingStarted.adoc b/plugin-core/docs/src/docs/introduction/gettingStarted.adoc index 116c18513..09a8c7a72 100644 --- a/plugin-core/docs/src/docs/introduction/gettingStarted.adoc +++ b/plugin-core/docs/src/docs/introduction/gettingStarted.adoc @@ -40,6 +40,9 @@ After installation, execute the `s2-quickstart` initialization script. This sets ./gradlew runCommand -Pargs="s2-quickstart com.yourapp User Role" ---- +If you are installing into a Grails plugin instead of an application, you must make sure you are using the `web-plugin` profile. Otherwise dependencies will not be met. +Running the same command will inject the spring beans into your `GrailsPlugin` classes `doWithSpring` method. + === Plugin Configuration and Setup The Spring Security plugin streamlines configuration and setup through a combination of steps: @@ -52,4 +55,4 @@ The Spring Security plugin streamlines configuration and setup through a combina The plugin configures Spring beans within the application context to implement various functionality components. Dependency management automatically handles the selection of appropriate jar files. -By following these steps, your Grails application will be ready to leverage the Spring Security plugin for enhanced security. While in-depth knowledge of Spring Security isn't mandatory, having a basic understanding of its underlying implementation can be helpful. For more details, refer to the [Spring Security documentation](https://{htmlsingle}). \ No newline at end of file +By following these steps, your Grails application will be ready to leverage the Spring Security plugin for enhanced security. While in-depth knowledge of Spring Security isn't mandatory, having a basic understanding of its underlying implementation can be helpful. For more details, refer to the [Spring Security documentation](https://{htmlsingle}). diff --git a/plugin-core/plugin/grails-app/commands/grails.plugin.springsecurity/S2QuickstartCommand.groovy b/plugin-core/plugin/grails-app/commands/grails.plugin.springsecurity/S2QuickstartCommand.groovy index 95c06bfd0..b1c2a93e0 100644 --- a/plugin-core/plugin/grails-app/commands/grails.plugin.springsecurity/S2QuickstartCommand.groovy +++ b/plugin-core/plugin/grails-app/commands/grails.plugin.springsecurity/S2QuickstartCommand.groovy @@ -77,8 +77,12 @@ Example: ./grailsw s2-quickstart --uiOnly initializeTemplateAttributes() createDomains(userModel, roleModel, requestmapModel, roleGroupModel) } - - updateConfig(userModel?.simpleName, roleModel?.simpleName, requestmapModel?.simpleName, userModel?.packageName, roleGroupModel != null) + if (new File('grails-app/conf/application.yml').exists()) { + consoleLogger.addStatus('\nConfiguring Spring Security; Updating YAML Application Config') + updateYmlConfig(userModel?.simpleName, roleModel?.simpleName, requestmapModel?.simpleName, userModel?.packageName, roleGroupModel != null) + } else { + updateConfig(userModel?.simpleName, roleModel?.simpleName, requestmapModel?.simpleName, userModel?.packageName, roleGroupModel != null) + } logStatus() return SUCCESS } @@ -205,7 +209,16 @@ Example: ./grailsw s2-quickstart --uiOnly List> beans = [] beans.add([import : "import ${userModel.packageName}.${userModel.simpleName}PasswordEncoderListener".toString(), definition: "${userModel.propertyName}PasswordEncoderListener(${userModel.simpleName}PasswordEncoderListener)".toString()]) - addBeans(beans, 'grails-app/conf/spring/resources.groovy') + + if(new File('grails-app/conf/spring/resources.groovy').exists()) { + addBeans(beans, 'grails-app/conf/spring/resources.groovy') + } else { + //we could be generating this in a plugin... we should look for a Plugin class + File pluginClassFile = findPluginClass() + if(pluginClassFile != null) { + addBeansToPlugin(beans, pluginClassFile) + } + } generateFile('Authority', roleModel.packagePath, roleModel.simpleName) @@ -267,6 +280,79 @@ Example: ./grailsw s2-quickstart --uiOnly } } + private void updateYmlConfig(String userClassName, String roleClassName, String requestmapClassName, String packageName, boolean useRoleGroups) { + //I mean, we could use SNAKE YAML TO GENERATE THIS BUT I DIGRESS + file('grails-app/conf/application.yml').withWriterAppend { BufferedWriter writer -> + writer.newLine() + writer.writeLine('---') + writer.writeLine('# Added by the Spring Security Core plugin:') + writer.writeLine('grails:') + writer.writeLine(' plugin:') + writer.writeLine(' springsecurity:') + if (!uiOnly) { + writer.writeLine(' userLookup:') + writer.writeLine(" userDomainClassName: '${packageName}.$userClassName'") + writer.writeLine(" authorityJoinClassName: '${packageName}.$userClassName$roleClassName'") + } + if(!uiOnly || useRoleGroups) { + writer.writeLine(' authority:') + if(!uiOnly) { + writer.writeLine(" className: '${packageName}.$roleClassName'") + } + if(useRoleGroups) { + writer.writeLine(" groupAuthorityNameField: authorities") + } + } + + if (useRoleGroups) { + writer.writeLine(' useRoleGroups: true') + } + if (requestmapClassName) { + writer.writeLine(" requestMap.className: '${packageName}.$requestmapClassName'") + writer.writeLine(" securityConfigType: Requestmap") + } + writer.writeLine(' controllerAnnotations:') + writer.writeLine(' staticRules:') + writer.writeLine(' - pattern: /') + writer.writeLine(' access: [\'permitAll\']') + writer.writeLine(' - pattern: /error') + writer.writeLine(' access: [\'permitAll\']') + writer.writeLine(' - pattern: /index') + writer.writeLine(' access: [\'permitAll\']') + writer.writeLine(' - pattern: /index.gsp') + writer.writeLine(' access: [\'permitAll\']') + writer.writeLine(' - pattern: /shutdown') + writer.writeLine(' access: [\'permitAll\']') + writer.writeLine(' - pattern: /assets/**') + writer.writeLine(' access: [\'permitAll\']') + writer.writeLine(' - pattern: /**/js/**') + writer.writeLine(' access: [\'permitAll\']') + writer.writeLine(' - pattern: /**/css/**') + writer.writeLine(' access: [\'permitAll\']') + writer.writeLine(' - pattern: /**/images/**') + writer.writeLine(' access: [\'permitAll\']') + writer.writeLine(' - pattern: /**/favicon.ico') + writer.writeLine(' access: [\'permitAll\']') + writer.newLine() + + writer.writeLine(' filterChain:') + writer.writeLine(' chainMap:') + writer.writeLine(' - pattern: /assets/**') + writer.writeLine(' filters: none') + writer.writeLine(' - pattern: /**/js/**') + writer.writeLine(' filters: none') + writer.writeLine(' - pattern: /**/css/**') + writer.writeLine(' filters: none') + writer.writeLine(' - pattern: /**/images/**') + writer.writeLine(' filters: none') + writer.writeLine(' - pattern: /**/favicon.ico') + writer.writeLine(' filters: none') + writer.writeLine(' - pattern: /**') + writer.writeLine(' filters: JOINED_FILTERS') + writer.newLine() + } + } + private void generateFile(String templateName, String packagePath, String className, String fileName = null, String folder = 'grails-app/domain') { render template(templateName + '.groovy.template'), file("${folder}/$packagePath/${fileName ?: className}.groovy"), @@ -301,5 +387,39 @@ Example: ./grailsw s2-quickstart --uiOnly } } + private void addBeansToPlugin(List> beans, File pluginClassFile) { + List lines = [] + if (pluginClassFile.exists()) { + pluginClassFile.eachLine { line, nb -> + lines << line + if(line.trim().startsWith('package ')) { + beans.forEach(bean -> lines.add(bean.import)) + } + if (line.contains('doWithSpring()')) { + beans.each { Map bean -> + lines << ' ' + bean.definition + } + } + } + } + + pluginClassFile.withWriter('UTF-8') { writer -> + lines.each { String line -> + writer.write "${line}${System.lineSeparator()}" + } + } + } + + + private File findPluginClass() { + File pluginClass = null + new File("src/main/groovy").eachFileRecurse { fl -> + if (fl.isFile() && fl.name.endsWith("GrailsPlugin.groovy")) { + pluginClass = fl + } + } + return pluginClass + } + }