Ability to add "plugin" in war-mode - grails

I am lookin for a way to let the admin-role of my grails-app add a "feature"/"plugin"
to the running server, so that the system makes use of it instantly.
To be more concrete here is a small example:
package domains
abstract class Provider {
def protected name;
def protected activated = false;
def private valid;
def Provider( providerName ) {
if( providerName != null ) {
name = providerName;
valid = true;
else valid = false;
}
def isValid() { valid }
def getName() { name }
def isActivated() { activated }
def setActivated( bool ) { activaed = bool }
abstract List<String> search( String searchKey );
}
Some Subclass:
package googleprovider
import Provider;
class GoogleProvider extends Provider {
def GooleProvider( active ) {
super( "Google" );
activated = active;
}
#Override
List<String> search( String searchKey ) {
return ["http://www.google.com"]
}
}
Now every "plugin"/"feature" should extend from Provider and be placed as what ever file in a directory "plugins/providers/".
And the server should create an instance of this GoogleProvider on an "onAdd"-event or something leashed by that admin.
Is there any chance this could be done? Or am I totally dreaming?
If it is somehow possible and it's just that I am going a completly wrong direction,
just tell me! ;-)
Thanks for your time!

I suggest you look for plugins that registers new Artefacts, so in your startup you can lookup for this classes. You can also create a folder in grails-app to store the providers classes.
See the Grails Dao Artefacts plugin, for example. It creates the daos folder inside grails-app, and consider all classes as a DAO Artefact. You also gain the ability of use Depenceny Injection in your classes (e.g. services).
Some points to look
Install Script creates the directory
You have some classes that declare the Artefact
The plugin descriptor is responsible to register them as Spring Beans
More info in
Grails developer wiki
Blog post with example

Related

How can I inject with Guice my api into dataflow jobs without needed to be serializable?

This question is a follow on after such a great answer Is there a way to upload jars for a dataflow job so we don't have to serialize everything?
This made me realize 'ok, what I want is injection with no serialization so that I can mock and test'.
Our current method requires our apis/mocks to be serialiable BUT THEN, I have to put static fields in the mock because it gets serialized and deserialized creating a new instance that dataflow uses.
My colleague pointed out that perhaps this needs to be a sink and that is treated differently? <- We may try that later and update but we are not sure right now.
My desire is from the top to replace the apis with mocks during testing. Does someone have an example for this?
Here is our bootstrap code that does not know if it is in production or inside a feature test. We test end to end results with no apache beam imports in our tests meaning we swap to any tech if we want to pivot and keep all our tests. Not only that, we catch way more integration bugs and can refactor without rewriting tests since the contracts we test are customer ones we can't easily change.
public class App {
private Pipeline pipeline;
private RosterFileTransform transform;
#Inject
public App(Pipeline pipeline, RosterFileTransform transform) {
this.pipeline = pipeline;
this.transform = transform;
}
public void start() {
pipeline.apply(transform);
pipeline.run();
}
}
Notice that everything we do is Guice Injection based so the Pipeline may be direct runner or not. I may need to modify this class to pass things through :( but anything that works for now would be great.
The function I am trying to get our api(and mock and impl to) with no serialization is thus
private class ValidRecordPublisher extends DoFn<Validated<PractitionerDataRecord>, String> {
#ProcessElement
public void processElement(#Element Validated<PractitionerDataRecord>element) {
microServiceApi.writeRecord(element.getValue);
}
}
I am not sure how to pass in microServiceApi in a way that avoid serialization. I would be ok with delayed creation as well after deserialization using guice Provider provider; with provider.get() if there is a solution there too.
Solved in such a way that mocks no longer need static or serialization anymore by one since glass bridging the world of dataflow(in prod and in test) like so
NOTE: There is additional magic-ness we have in our company that passes through headers from service to service and through dataflow and that is some of it in there which you can ignore(ie. the RouterRequest request = Current.request();). so for anyone else, they will have to pass in projectId into getInstance each time.
public abstract class DataflowClientFactory implements Serializable {
private static final Logger log = LoggerFactory.getLogger(DataflowClientFactory.class);
public static final String PROJECT_KEY = "projectKey";
private transient static Injector injector;
private transient static Module overrides;
private static int counter = 0;
public DataflowClientFactory() {
counter++;
log.info("creating again(usually due to deserialization). counter="+counter);
}
public static void injectOverrides(Module dfOverrides) {
overrides = dfOverrides;
}
private synchronized void initialize(String project) {
if(injector != null)
return;
/********************************************
* The hardest part is this piece since this is specific to each Dataflow
* so each project subclasses DataflowClientFactory
* This solution is the best ONLY in the fact of time crunch and it works
* decently for end to end testing without developers needing fancy
* wrappers around mocks anymore.
***/
Module module = loadProjectModule();
Module modules = Modules.combine(module, new OrderlyDataflowModule(project));
if(overrides != null) {
modules = Modules.override(modules).with(overrides);
}
injector = Guice.createInjector(modules);
}
protected abstract Module loadProjectModule();
public <T> T getInstance(Class<T> clazz) {
if(!Current.isContextSet()) {
throw new IllegalStateException("Someone on the stack is extending DoFn instead of OrderlyDoFn so you need to fix that first");
}
RouterRequest request = Current.request();
String project = (String)request.requestState.get(PROJECT_KEY);
initialize(project);
return injector.getInstance(clazz);
}
}
I suppose this may not be what you're looking for, but your use case makes me think of using factory objects. They may depend on the pipeline options that you pass (i.e. your PipelineOptions object), or on some other configuration object.
Perhaps something like this:
class MicroserviceApiClientFactory implements Serializable {
MicroserviceApiClientFactory(PipelineOptions options) {
this.options = options;
}
public static MicroserviceApiClient getClient() {
MyPipelineOptions specialOpts = options.as(MySpecialOptions.class);
if (specialOpts.getMockMicroserviceApi()) {
return new MockedMicroserviceApiClient(...); // Or whatever
} else {
return new MicroserviceApiClient(specialOpts.getMicroserviceEndpoint()); // Or whatever parameters it needs
}
}
}
And for your DoFns and any other execution-time objects that need it, you would pass the factory:
private class ValidRecordPublisher extends DoFn<Validated<PractitionerDataRecord>, String> {
ValidRecordPublisher(MicroserviceApiClientFactory msFactory) {
this.msFactory = msFactory;
}
#ProcessElement
public void processElement(#Element Validated<PractitionerDataRecord>element) {
if (microServiceapi == null) microServiceApi = msFactory.getClient();
microServiceApi.writeRecord(element.getValue);
}
}
This should allow you to encapsulate the mocking functionality into a single class that lazily creates your mock or your client at pipeline execution time.
Let me know if this matches what you want somewhat, or if we should try to iterate further.
I have no experience with Guice, so I don't know if Guice configurations can easily pass the boundary between pipeline construction and pipeline execution (serialization / submittin JARs / etc).
Should this be a sink? Maybe, if you have an external service, and you're writing to it, you can write a PTransform that takes care of it - but the question of how you inject various dependencies will remain.

Grails schema multitenancy mode: GORM listeners only working with default tenant

I'm adapting an existing Grails 3 project to a multi-tenant structure, using the schema mode provided by GORM, and I'm having trouble getting the GORM listeners to work when I specify a tenant.
My listener looks like this:
#CompileStatic
class VehicleListenerService {
#Listener(Vehicle)
void onPreInsertEvent(PreInsertEvent event) {
println "*** Vehicle preInsert"
event.entityAccess.setProperty('model', 'preInsert')
}
#Listener(Vehicle)
void onPreUpdateEvent(PreUpdateEvent event) {
println "*** Vehicle preUpdate"
event.entityAccess.setProperty('model', 'preUpdate')
}
}
So every time a vehicle is created or updated, its model should be changed to preInsert or preUpdate.
The current tenant is determined by the subdomain specified in the URL. If I access the app with no subdomain (via http://localhost:8080), the listener works as expected, but if I provide a subdomain (http://test.localhost:8080), the listener doesn't do anything, and the vehicle model doesn't change.
What do I have to do to make the GORM listener work with any tenant?
I've created a sample project (https://github.com/sneira/mtschema) which reproduces the error.
With help from the Grails Slack channel and some more research, I've come up with a solution to this.
First, the listener service has to extend AbstractPersistenceEventListener:
#CompileStatic
class VehicleListenerService extends AbstractPersistenceEventListener {
protected VehicleListenerService(Datastore datastore) {
super(datastore)
}
#Override
protected void onPersistenceEvent(AbstractPersistenceEvent event) {
String newModel =
event.eventType == EventType.PreInsert ? 'preInsert' : 'preUpdate'
event.entityAccess.setProperty('model', newModel)
}
#Override
boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
boolean supportsEvent = eventType.isAssignableFrom(PreInsertEvent) ||
eventType.isAssignableFrom(PreUpdateEvent)
return supportsEvent
}
}
Now we can create a service instance for each schema (except for the default) in Bootstrap.groovy, and add it to our app:
def init = { servletContext ->
def ctx = grailsApplication.mainContext
['TEST', 'TEST2'].each { String name ->
HibernateDatastore ds = hibernateDatastore.getDatastoreForConnection(name)
VehicleListenerService listener = new VehicleListenerService(ds)
ctx.addApplicationListener(listener)
}
}
I've uploaded the complete code to https://github.com/sneira/mtschema/tree/listeners.

XText cross-reference to an non-DSL resource

Please consider this minimal Xtext grammar.
Model:
"As a" stackeholder=Stakeholder "I want" want=Want;
Stakeholder:
'client' | 'developer' | 'manager';
Want:
'everything' | 'cookies' | 'fame';
Now what I need to do, is to move the definition of stakeholders (let's forget about want) to SOME external data source. This "external data source" might be a CSV file, might be a DB or maybe a web service. But I it is highly unlikely to be some Xtext file or to come with an EMF model. But still I want to cross-reference it just like you can cross-reference java types in your DSL.
Issues like manual parsing and caching (for performance sake) aside: is this even doable?
I've dug a little into the topic of scopes and resource providers but everything I found required the external source to be part of at least another DSL.
I'd be very happy about a rough outline what would be needed to be done.
Sorry it took me so long to respond. I tried Christians suggestion, was not very satisfied and than priorities shifted. Now I'll have another go at the problem and in order to document for others (and to clear my head) I'll write down what I did so far since it was not all that straight forward and required a fair amount of experimentation.
I will not post full classes but only the relevant parts. Feel free to ask for more detail if you need it.
My Syntax-Definition now looks like this:
Model:
stakeholders+=StakeholderDecl*
requirements+=Requirement*;
Requirement:
'As a' stakeholder=[Stakeholder] 'I want' want=('everything' | 'cookies' | 'results')
;
StakeholderDecl returns Stakeholder :
'Stakeholder' Stakeholder
;
Stakeholder:
name=ID
;
Let it be noted that everything below needed to to be done in the .ui package.
First I created StakeholdersProvider.xtend:
class StakeholdersProvider extends AbstractResourceDescription {
// this is the dummy for an "external source". Just raw data.
val nameList = newArrayList( "buddy", "boss" )
val cache = nameList.map[it.toDescription]
private val uri = org.eclipse.emf.common.util.URI.createPlatformResourceURI("neverland", true)
def public List<IEObjectDescription> loadAdditionalStakeholders() {
cache
}
def private IEObjectDescription toDescription(String name) {
ExternalFactoryImpl.init()
val ExternalFactory factory = new ExternalFactoryImpl()
val Stakeholder obj = factory.createStakeholder as StakeholderImpl
obj.setName(name)
new StakeholderDescription(name, obj, uri)
}
. . .
override getURI() {
uri
}
def public boolean isProvided( EObject object ) {
if( object.eClass.classifierID != ExternalPackageImpl.STAKEHOLDER ) {
false
}
else {
val stakeholder = object as Stakeholder
nameList.exists[it == stakeholder.name]
}
}
}
note that the provider is also a resourceDescription and its uri of course is nonsense.
With this provider I wrote a ScopeWrapper.xtend :
class ScopeWrapper implements IScope {
private var IScope scope;
private var StakeholdersProvider provider
new( IScope scopeParam, StakeholdersProvider providerParam ) {
scope=scopeParam
provider = providerParam
}
override getAllElements() {
val elements = scope.allElements.toList
val ret = provider.loadAdditionalStakeholders()
ret.addAll(elements)
ret
}
override getSingleElement(QualifiedName name) {
allElements.filter[it.name == name].head
}
. . .
}
and ResourceDescriptionWrapper.xtend
class ResourceDescriptionsWrapper implements IResourceDescriptions {
private StakeholdersProvider provider;
private IResourceDescriptions descriptions;
new(IResourceDescriptions descriptionsParam, StakeholdersProvider providerParam) {
descriptions = descriptionsParam
provider = providerParam
}
override getAllResourceDescriptions() {
val resources = descriptions.allResourceDescriptions.toList
resources.add(provider)
resources
}
override getResourceDescription(URI uri) {
if( uri == provider.URI ) provider
else descriptions.getResourceDescription(uri)
}
override getExportedObjects() {
val descriptions = descriptions.exportedObjects.toList
descriptions.addAll(provider.exportedObjects)
descriptions
}
. . . some overrides for getExportedObjects-functions
}
all of this is wired together MyGlobalScopeProvider.xtend
class MyGlobalScopeProvider extends TypesAwareDefaultGlobalScopeProvider {
val provider = new StakeholdersProvider()
override getScope(Resource context, EReference reference, Predicate<IEObjectDescription> filter) {
val scope = super.getScope(context, reference, filter)
return new ScopeWrapper(scope, provider)
}
override public IResourceDescriptions getResourceDescriptions(Resource resource) {
val superDescr = super.getResourceDescriptions(resource)
return new ResourceDescriptionsWrapper(superDescr, provider)
}
}
which is registered in MyDslUiModule.java
public Class<? extends IGlobalScopeProvider> bindIGlobalScopeProvider() {
return MyGlobalScopeProvider.class;
}
So far so good. I now get boss and buddy suggested as stakeholders. However when I use one of those 2 I get an error in the editor complaining about a dangling reference and an error logging in the console that a stakeholder cannot be exported as the target is not contained in a resource. Figuring those 2 might are related I tried to fix the error logging, created MyresourceDescriptionStrategy.xtend
class MyResourcesDescriptionStrategy extends DefaultResourceDescriptionStrategy {
val provider = new StakeholdersProvider()
override isResolvedAndExternal(EObject from, EObject to) {
if (provider.isProvided(to)) {
// The object is a stakeholder that was originally provided by
// our StakeholdersProvider. So we mark it as resolved.
true
} else {
super.isResolvedAndExternal(from, to)
}
}
}
and also wire it in the UiModule:
public Class<? extends IDefaultResourceDescriptionStrategy> bindDefaultResourceDescriptionStrategy() {
return MyResourcesDescriptionStrategy.class;
}
This fixes the logging error but the "dangling reference" problem remains. I searched for solutions for this and the most prominent result suggests that defining a IResourceServiceProvider would have been the best way to solve my problem in the first place.
I'll spend a bit more time on the current approach and than try it with a ResourceProvider.
EDIT: I got the "dangling reference" problem fixed. The loadAdditionalStakeholders() function in StakeholdersProvider.xtend now looks like this:
override loadAdditionalStakeholders() {
val injector = Guice.createInjector(new ExternalRuntimeModule());
val rs = injector.getInstance(ResourceSet)
val resource = rs.createResource(uri)
nameList.map[it.toDescription(resource)]
}
def private IEObjectDescription toDescription(String name, Resource resource) {
ExternalFactoryImpl.init()
val ExternalFactory factory = new ExternalFactoryImpl()
val Stakeholder obj = factory.createStakeholder as StakeholderImpl
obj.setName(name)
// not sure why or how but when adding the obj to the resource, the
// the resource will be set in obj . . . thus no more dangling ref
resource.contents += obj
new StakeholderDescription(name, obj, uri)
}

springsecurity with grails - how to deny login for users that are deleted using a logical deleted property

I am using grails 2.2.2 and spring-security-core:2.0-RC2
My domain User object does not actually get deleted from the database - instead i set a property called "deleted" to true.
The problem is: how can I stop spring security from granting successful login attempts to a user id that is marked as deleted?
I'd like to be able to support creating a new user with a previously deleted name.
Burt Beckwith's answer below got me on the track of checking out overriding the spring security bean implementaions.
I've tried overriding a couple of the methods in the userDetailsService bean and springSecurityService with my implementations as shown below (all I did was use the same parent class implementation but change the User.findWhere() methods to use deleted: false).
I've also added these bean definitions to resources.groovy but i find that sometimes the original SpringSecurityService.getCurrentUser() method is called instead of my implementation. (If i change the spring security source to my implementation this all works fine, but I'd rather use an override so future version upgrades don't break).
class MySpringSecurityService extends SpringSecurityService {
#Override
Object getCurrentUser() {
if (!isLoggedIn()) {
return null
}
String className = SpringSecurityUtils.securityConfig.userLookup.userDomainClassName
String usernamePropName = SpringSecurityUtils.securityConfig.userLookup.usernamePropertyName
grailsApplication.getClassForName(className).findWhere(
(usernamePropName): principal.username,
deleted: false)
}
}
class MyUserDetailsService extends GormUserDetailsService {
UserDetails loadUserByUsername(String username, boolean loadRoles) throws UsernameNotFoundException {
def conf = SpringSecurityUtils.securityConfig
String userClassName = conf.userLookup.userDomainClassName
def dc = grailsApplication.getDomainClass(userClassName)
if (!dc) {
throw new IllegalArgumentException("The specified user domain class '$userClassName' is not a domain class")
}
Class<?> User = dc.clazz
User.withTransaction { status ->
def user = User.findWhere((conf.userLookup.usernamePropertyName): username, deleted: false)
if (!user) {
log.warn "User not found: $username"
throw new NoStackUsernameNotFoundException()
}
Collection<GrantedAuthority> authorities = loadAuthorities(user, username, loadRoles)
createUserDetails user, authorities
}
}
}
My resources.groovy looks something like this:
beans = {
userDetailsService(MyUserDetailsService)
springSecurityService(MySpringSecurityService) {
authenticationTrustResolver = ref('authenticationTrustResolver')
grailsApplication = ref('grailsApplication')
passwordEncoder = ref('passwordEncoder')
objectDefinitionSource = ref('objectDefinitionSource')
userDetailsService = ref('userDetailsService')
userCache = ref('userCache')
}
}
This isn't tested, but should work or be very close to working.
You'll need to override the logic in the preAuthenticationChecks Spring bean that the plugin registers. The default implementation does the isAccountNonLocked(), isEnabled(), and isAccountNonExpired() checks, so you can subclass it and add your check(s):
package com.foo.bar
import grails.plugin.springsecurity.userdetails.DefaultPreAuthenticationChecks
import org.springframework.security.authentication.AccountStatusException
class MyPreAuthenticationChecks extends DefaultPreAuthenticationChecks {
private static final EXCEPTION = new DeletedException()
void check(UserDetails user) {
// do the standard checks
super.check user
// then the custom check(s)
if (user.deleted) {
log.debug 'User account is deleted'
throw EXCEPTION
}
}
static class DeletedException extends AccountStatusException {
LockedException() {
super('User account is deleted')
}
// avoid the unnnecessary cost
Throwable fillInStackTrace() {
this
}
}
}
Put this in a subdirectory of your /src/groovy directory corresponding to the package you choose, and register yours in grails-app/conf/spring/resources.groovy:
import com.foo.bar.MyPreAuthenticationChecks
beans = {
preAuthenticationChecks(MyPreAuthenticationChecks)
}
Note that the "user" instance you'll be working with is a UserDetails instance, not the domain class instance that is loaded to populate the UserDetails. So to have access to the deleted property you'll also need a custom UserDetailsService. This is easy and a common enough thing to do that it has its own chapter in the docs: http://grails-plugins.github.io/grails-spring-security-core/docs/manual/guide/userDetailsService.html
Note that having the exception as a static inner class, using a singleton, and overriding the fillInStackTrace method are all optional and independent of this question. You can put it in its own top-level class if you prefer (it makes sense to me to keep it internal since it's unlikely to be used outside of this class). You can also create a new instance each time, but there's no difference between instances (there's no useful state) so that's not needed. I override to fillInStackTrace to avoid incurring the cost of filling all those stack frames when they're not going to be needed at all.
Late, but if it serves somebody, here it is:
Just simplifying Burt Beckwith's answer, if you just want to throw the same exception as if the user does not exist, you can just extend DefaultPreAuthenticationChecks like this (where Person is the table with the users), and there is no need for a custom UserDetailsService:
import grails.plugin.springsecurity.userdetails.DefaultPreAuthenticationChecks;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
class MyPreAuthenticationChecks extends DefaultPreAuthenticationChecks {
void check(UserDetails user) {
// do the standard checks
super.check user
if (Person.findByUsername(user.getUsername())?.deleted){
log.debug("Wrong credentials");
throw new UsernameNotFoundException("Wrong credentials");
}
}
}

Role based domain class field access in grails

I am developing a grails application.In that some cases I want to control the domain class fields based on the role.So that in each call to getter setter method of domain class I want to apply some filter based on role(Logged in user's role).I am assuming that grails will create getter setter method at runtime for the domin classes.So while writing grails code is it possible to apply this logic.If it is possible then how to apply?
Example:
Domain Class :
class Book{
String name;
double price;
}
Controller:
def index={
Book book=Book.get(1);
println book.name;
println book.price;
}
In the above code "println book.price;" this line should work only for particular role.For some other role it should throw some exception.
Is it possible achieve?Is there any plugin to do this?
Please give some help on this....Thanks
You can create get/set methods for the properties you want to control access to and put your security logic there. Assuming you've written your own security service or are using a security plugin like the Spring Security (Acegi) plugin you would:
class Book{
String name;
double price;
def authenticateService
void setPrice(double price) {
if(!authenticateService.ifAllGranted('ROLE_PRICE_FIXER')) {
throw new Exception("You are not authorized to set book prices")
}
this.price = price
}
double getPrice() {
if(!authenticateService.ifAllGranted('ROLE_PRICE_FIXER')) {
throw new Exception("You are not authorized to get book prices")
}
return this.price
}
}
I am not aware of any plugin that allows access controls to be put on domain properties.
You could also consider using a custom validator or a spring errors object to catch attempts to set a field before saving it.
EDIT: Here is an example of what I was thinking. You could generalize quite a bit more and the code here hasn't been tested so it probably won't run as is.
class securedDomain {
String securedField
def fieldSetBy = [:]
def previousValue = [:]
static transients = ['fieldSetBy', 'previousValue']
static constraints = {
securedField(validator: { v, o ->
def access = User.findByName(fieldSetBy['securedField']).hasAccess('securedField')
if(!access) securedField = previousValue['securedField']
return access
})
void setProperty(String name, value) {
if(name == "securedField") {
fieldSetBy['securedField'] = session.user
previousValue['securedField'] = securedField
securedField = value
} else {
super(name, value)
}
}

Resources