I have a very simple service set up to create an entry in a Postgres table, and I use it in Bootstrap.groovy for my Grails 3 web-app.
// CompanyService
public Company createCompany(String name) {
Company company = new Company(name: name)
company.save()
return company
}
// BootStrap
def init = {
companyService.createCompany('My Company')
}
Well, at startup I cannot see My Company entry, no matter if the service is transactional or not.
Instead, if using the same line for example in a controller, it works as expected. Am I missing something here?
Have you called your service inside bootstrap?
class BootStrap {
def companyService
def init = { servletContext ->
companyService.createCompany('My Company')
}
}
Related
I want to create a custom permission handler using grails spring security plugin.
Imagine I have a User class and a company class with a many-to-many association.
I want only allow users to call a method called "delete company" when they belong to the company. Example:
class User {
static hasMany= [companies:Company]
static belongsTo = [Company]
}
class Company {
static hasMany = [users:User]
}
the controller action looks like to following:
def deleteCompany(Long id) {
}
I only want to allow users to call the method that are part of the company. So when
assert Company.get(id}.users.find { it == currentUser }
This is just a simplified example. The actual structure is much more complex. That's why I want to use the power of spring security for this.
I already played around with spring security ACL but it seems that I can only use custom permissions in services but not in controllers
You could use beforeInterceptor in your controller:
def springSecurityService
def beforeInterceptor=[action:this.&auth]
private auth = {
def toBeCheckedId=params.id
if(toBeCheckedId
&& Company.get(toBeCheckedId}.users.find { it == springSecurityService.currentUser }){
redirect action:someHandlingAction
return false
}
}
}
Is there any way to define the users that can use my application in a list in Config.groovy? This will be using Grails 2.2.3 and the latest versions of Spring Security Core and Spring Security LDAP.
We use Active Directory for authentication, and only 2 or 3 people will use this little application, so it doesn't seem worthy of making an AD Group for just this app. It would be simpler to define a list, and any time there is a new hire instead of adding them to the AD group all I have to do is add their name to the external Grails config.
I would like to do something like the following:
SomeController.groovy
#Secured("authentication.name in grailsApplication.config.my.app.usersList")
class SomeController {
}
Then in Config.groovy put this code:
my.app.usersList = ['Bill', 'Tom', 'Rick']
Is this possible? If so, is this a terrible idea? Thanks a lot.
That seems really silly. Why not have the list of users in a table? Then you can add/remove from that table without have to modify the application.
I currently do this and in my UserDetailsContextMapper I make sure the username already exists in the Users table.
You need a custom authenticator that will try to access your Active Directory and if authenticated, will look into Grails properties to check if the username is allowed to login.
This is the class that I use. I changed the code to validate the config:
class ActiveDirectoryAuthenticator {
private DefaultSpringSecurityContextSource contextFactory
private String principalSuffix = ""
def grailsApplication
public DirContextOperations authenticate(Authentication authentication) {
// Grab the username and password out of the authentication object.
String principal = authentication.getName() + "#" + principalSuffix
String password = ""
if (authentication.getCredentials() != null) {
password = authentication.getCredentials().toString()
}
// If we have a valid username and password, try to authenticate.
if (!("".equals(principal.trim())) && !("".equals(password.trim()))) {
try {
String provider = contextFactory.getUrls()[0]
Hashtable authEnv = new Hashtable(11)
authEnv.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap.LdapCtxFactory")
authEnv.put(Context.PROVIDER_URL, provider)
authEnv.put(Context.SECURITY_AUTHENTICATION, "simple")
authEnv.put(Context.SECURITY_PRINCIPAL, principal)
authEnv.put(Context.SECURITY_CREDENTIALS, password)
javax.naming.directory.DirContext authContext = new InitialDirContext(authEnv)
//here validate the user against your config.
if(!authentication.getName() in grailsApplication.config.adUsersAllowed) {
throw new BadCredentialsException("User not allowed.")
}
DirContextOperations authAdapter = new DirContextAdapter()
authAdapter.addAttributeValue("ldapContext", authContext)
return authAdapter
} catch ( NamingException ex ) {
throw new BadCredentialsException(ex.message)
}
} else {
throw new BadCredentialsException("Incorrect username or password")
}
}
public DefaultSpringSecurityContextSource getContextFactory() {
return contextFactory
}
/**
* Set the context factory to use for generating a new LDAP context.
*
* #param contextFactory
*/
public void setContextFactory(DefaultSpringSecurityContextSource contextFactory) {
this.contextFactory = contextFactory
}
public String getPrincipalSuffix() {
return principalSuffix
}
/**
* Set the string to be prepended to all principal names prior to attempting authentication
* against the LDAP server. (For example, if the Active Directory wants the domain-name-plus
* backslash prepended, use this.)
*
* #param principalPrefix
*/
public void setPrincipalSuffix(String principalSuffix) {
if (principalSuffix != null) {
this.principalSuffix = principalSuffix
} else {
this.principalSuffix = ""
}
}
}
Declare it as your ldapAuthenticator in resources.groovy:
ldapAuthenticator(ActiveDirectoryAuthenticator) {
contextFactory = ref('contextSource')
principalSuffix = 'domain.local' //your domain suffix
grailsApplication = ref('grailsApplication')
}
The downside is that you need to restart your context when you change config.groovy
In your controllers just use #Secured('IS_AUTHENTICATED_FULLY')
I do not think you can do that because annotations are resolved at compile time and not in runtime. Config properties will be read during the application runtime so you I fear you have to end up doing:
#Secured(["authentication.name in ['Bill', 'Tom', 'Rick']"])
class SomeController {
}
If I remember correctly the #Secured annotation cannot be used for other things than comparing roles. But you should be able to do this with spring securities #PreAuthorize and #PostAuthorize annotations. When using grails the easiest way to setup these annotations is installing the spring security ACL plugin.
Within #PreAuthorize and #PostAuthorize you can use SPEL expressions which are more flexible. Unfortunatelly SPEL does not provide an in operator. However you can delegate the security check to a service:
#PreAuthorize('#securityService.canAccess(authentication)')
public void test() {
println "test?"
}
With the # symbol you can reference other beans like services within expression. Here the method securityService.canAccess() is called to evaluate if the logged in user can access this method.
To use this you have to configure a BeanResolver. I wrote some more details about configuring a BeanResolver here.
Within securityService you can now do:
class SecurityService {
def grailsApplication
public boolean canAccess(Authentication auth) {
return grailsApplication.config.myList.contains(auth.name)
}
}
In general I would not recommend to use a configuration value for validating the user in security checks. The groovy configuration will be compiled so you cannot easily add a new user without redeploying your application.
My Integration-Test for my grails application is returning a null object when I try to get a domain object using grails dynamic get method.
This is a simplified example of my problem. Lets say I have a controller TrackerLogController that uses a service TrackerLogService to save an updated Log domain for another Tracker domain.
Domain Tracker:
class Tracker {
int id
String name
static hasMany = [logs: Log]
}
Domain Log:
class Log {
int id
String comment
static belongsTo = [tracker: Tracker]
}
Controller TrackerLogController save:
def TrackerLogService
def saveTrackerLog() {
def trackerId = params.trackerId
def trackerInstance = Tracker.get(trackerId)
Log log = TrackerLogService.saveTrackerLogs(trackerInstance, params.comment)
if( log.hasErrors() ){
//render error page
}
//render good page
}
Service TrackerLogService save:
Log saveTrackerLogs( Tracker tracker, String comment) {
Log log = new Log(tracker: tracker, comment: comment)
log.save()
return log
}
So now I want to write an Integration-Test for this service but I'm not sure if I should be writing one just for the simple logic in the controller (if error, error page else good page) I would think I would write a Unit test for that, and an Integration-Test to check the persistence in the Database.
This is what I have for my Integration-Test:
class TrackerLogServiceTests {
def trackerLogService
#Before
void setUp(){
def tracker = new Tracker(id: 123, name: "First")
tracker.save()
//Now even if I call Tracker.get(123) it will return a null value...
}
#Test
void testTrackerLogService() {
Tacker trackerInstance = Tracker.get(123) //I have tried findById as well
String commit = "This is a commit"
//call the service
Log log = trackerLogService.saveTrackerLogs(trackerInstance , commit)
//want to make sure I added the log to the tracker Instance
assertEquals log , trackerInstance.logs.findByCommit(commit)
}
}
So for this example my trackerInstance would be a null object. I know the Grails magic doesn't seem to work for Unit tests without Mocking, I thought for Intigration-Tests for persistence in the DB you would be able to use that grails magic.
You can't specify the id value unless you declare that it's "assigned". As it is now it's using an auto-increment, so your 123 value isn't used. It's actually ignored by the map constructor for security reasons, so you'd need to do this:
def tracker = new Tracker(name: "First")
tracker.id = 123
but then it would get overwritten by the auto-increment lookup. Use this approach instead:
class TrackerLogServiceTests {
def trackerLogService
private trackerId
#Before
void setUp(){
def tracker = new Tracker(name: "First")
tracker.save()
trackerId = tracker.id
}
#Test
void testTrackerLogService() {
Tacker trackerInstance = Tracker.get(trackerId)
String commit = "This is a commit"
//call the service
Log log = trackerLogService.saveTrackerLogs(trackerInstance , commit)
//want to make sure I added the log to the tracker Instance
assertEquals log , trackerInstance.logs.findByCommit(commit)
}
}
Also, unrelated - don't declare the id field unless it's a nonstandard type, e.g. a String. Grails adds that for you, along with the version field. All you need is
class Tracker {
String name
static hasMany = [logs: Log]
}
and
class Log {
String comment
static belongsTo = [tracker: Tracker]
}
i use grails-1.3.2 and gorm-hbase-0.2.4 plugin.
Sometimes i need to change tables structure(add new tables or columns).
I have created Car table:
class Car{
static belongsTo = [user:User]
String color
String model
//.....
static constraints = {
}
}
but when i want to create car object:
def create = {
Car car = new Car()
car.properties = params
car.save(flush: true)
}
I got the following exception:
ERROR gorm.SavePersistentMethod - APP_CAR
org.apache.hadoop.hbase.TableNotFoundException: APP_CAR
After i run application with create-drop, everithing starts work good..
but i can not after every changes delete all data,
i thought plugin have to do all updates
so, i am looking some way after changind tables structure continue to run application without drop tables..
If anybody know solution please help.
Grails will NOT do automatic updates to your tables, what if it drops a column in production automatically? Maybe that is not what you wanted.
There is a database migration plugin to do this and here is an excellent link that explains it. Note that you need to use grails prod instead of using the ones directly in the link, otherwise it will run in development mode only. The link does not show prod in its commands.
The official links are here and the spring source blog about this is here.
database migration plugin will not be works, because it works only with hibernate.
You need to do some changes in plugin source. HBasePluginSupport.grovy
static doWithApplicationContext = {ApplicationContext applicationContext ->
LOG.debug("Closure HBasePluginSupport.doWithApplicationContext{} invoked with arg $applicationContext")
assert !PluginManagerHolder.getPluginManager().hasGrailsPlugin("hibernate"),"hibernate plug-in conflicts with gorm-hbase plug-in"
// Read data source configuration, setting defaults as required
def dataSource = application.config.dataSource
// TODO write tests for this <--- Even maybe figure out if this is ever invoked
if (!dataSource) dataSource = new HBaseDefaults()
def dbCreate = dataSource?.dbCreate
if (!dbCreate) dbCreate = "create-drop"
LOG.debug("Data Source configured with dbCreate set to $dbCreate")
// TODO Complete dbCreate related processing
if (dbCreate?.toUpperCase()?.equals("CREATE-DROP")) {
def createIndexedTables = dataSource?.indexed
LOG.debug ("Flag createIndexedTables set to $createIndexedTables")
def tableManager = HBaseLookupUtils.getBean("hbase.table.manager")
tableManager.createSequenceTable()
tableManager.createReferenceTable()
application.domainClasses.each {domainClass ->
LOG.debug("Adding table for Domain Class $domainClass")
tableManager.createDomainTable(domainClass, createIndexedTables)
}
LOG.debug("List of all store found :")
tableManager.getTableNames().each {tn ->
LOG.debug("- $tn")
}
} else if (dbCreate?.toUpperCase()?.equals("UPDATE")) {
def createIndexedTables = dataSource?.indexed
def tableManager = HBaseLookupUtils.getBean("hbase.table.manager")
def existingTables = tableManager.getTableNames();
application.domainClasses.each {domainClass ->
LOG.debug("Domain Class $domainClass")
def tableDesc = new HTableDescriptor(HBaseNameUtils.getDomainTableName(domainClass))
if (!existingTables.contains(tableDesc.getNameAsString())) {
tableManager.createDomainTable(domainClass, createIndexedTables)
LOG.debug("Adding table for Domain Class $domainClass")
}
}
}
application.domainClasses.each {domainClass ->
LOG.debug("Adding dbms related methods to Domain Class $domainClass")
def domainClassManager = new HBaseDomainClassManager()
domainClassManager.createQueryMethods(domainClass)
domainClassManager.createPersistenceMethods(domainClass)
domainClassManager.addLazyLoadingSupport(domainClass)
domainClassManager.addDynamicFinders(domainClass)
}
}
I am trying to set a variable for the current user (a POJO) in all views so I can get things like the user name and check their role on every view (including the default layout). How can I setup something (e.g. currentUser) in grails so that it is accessible in every grails view like so:
<div>${currentUser.name}</div>
or like this:
<g:if test="${currentUser.admin}">ADMIN</g:if>
You want to use a grails filter. Using a filter, you can specify which controllers and methods (using wild cards) you want to intercept using before/after and afterView methods.
This makes it easy to stuff a new variable into the model so it's available in a view. Here's an example that uses the acegi plugin authenticateService:
class SecurityFilters {
def authenticateService
def filters = {
all(controller:'*', action:'*') {
after = { model ->
def principal = authenticateService.principal()
if (principal != null && principal != 'anonymousUser') {
model?.loggedInUser = principal?.domainClass
log.debug("SecurityFilter: adding current user to model = $model")
} else {
log.debug("SecurityFilter: anonymous user, model = $model")
}
}
}
}
}
You can use the session scope to store the variable. Your calls would change to:
<div>${session.currentUser.name}</div>
and
<g:if test="${session.currentUser.admin}">ADMIN</g:if>
And you would set the variable like so in a controller:
session.currentUser = XXXXX