Grails 3.0.1 here. I'm looking to accomplish a specific URL/controller structure. My app deploys at the root context (/), meaning locally it runs as http://localhost:8080, and non-locally as http://someserver.example.org.
I want everything under /app/* to be authenticated and considered to be part of the "core app" (requires login). Anything outside of that URL is considered to be a part of the public website (unauthenticated). However, I want /app/ itself to just be a placeholder of sorts; I do not want it to be a Grails controller. Hence:
http://localhost:8080/app may be configured (UrlMappings?) to bring up a login page
http://localhost:8080/app/<controller>/<action> follows typical Grails controller/action suit, and would invoke the correct controller action
Hence http://localhost:8080/app/order/create would be authenticated and, if logged in, invoke the OrderController#create action, which might render a createOrder.gsp.
I'm wondering what the Grails 3.x approach is to:
Allowing /app/ to exist, but not as a controller (like I said, perhaps just redirecting/mapping to a login page)
Allowing anything underneath /app/ to follow the controller/action paradigm
Thoughts on how to implement this?
Update
class UrlMappings {
static mappings = {
"/$controller/$action?/$id?(.$format)?"{
constraints {
// apply constraints here
}
}
"/app/$controller/$action?/$id?" {
???
}
"/"(view:"/index")
"500"(view:'/error')
"404"(view:'/notFound')
}
}
Grails has the possibility to declare namespaces for Controllers. With this, you can put all your controllers under the namespace 'app', which should result in exactly your second question. See docs for more details.
The security restriction then be accomplished with normal spring security settings (#Secured e.g).
Related
I have a Grails controller with multiple actions. For now all actions are available for user calls (I can access them from my browser), even the ones that should be called only from withing the g:include tag. I want to restrict access to such actions from the browser. I cannot mark action as protected because in this case I will not be able to include this action in a view for another controller.
Is there any practice how to encapsulate actions in such situations?
The way to “protect” actions from being accessible via a URL is to not provide a URL mapping to them. The default url mapping looks something like this…
class UrlMappings {
static mappings = {
"/$controller/$action?/$id?(.$format)?"{
constraints {
// apply constraints here
}
// ...
}
}
That “/$controller/$action?/$id?(.$format)?” mapping is convenient for simple crud apps and demos well but for any substantial app you should almost always remove that. Without it, only the actions you explicitly expose are accessible.
Anybody who already have implemented something similar using Grails could tell me please which are the good pratices (if there are any) to create user profile URLs with the format "http://www.myservice.com/username", as in Facebook, Twitter, Linkedin?
I'm trying to implement it through the UrlMappings and appears to me I'll need to break with the code conventions, at least for the Controllers.
So, any suggestions are welcome, thanks.
UPDATE 1
When I mentioned my concern about breaking the code conventions, what I'm saying is that I want to show the user profile using this mapping, but I do have other objects in my application which I would like to access using the default mapping:
"/$controller/$action?/$id?"()
SOLUTION
Thanks to the great contributions I've received here, I've camed up with this solution, which solves my problem.
As was pointed out, to do this kind of mapping, I'll need to control more closely how my requests are handled. That means I'll need to tell to Grails which controllers I don't want to be mapped to the "username" rule.
Since that will be a very tedious task (because I have several controllers), I did this to automate it:
UrlMappings.groovy
static mappings = {
getGrailsApplication().controllerClasses.each{ controllerClass ->
"/${controllerClass.logicalPropertyName}/$action?/$id?"(controller: controllerClass.logicalPropertyName)
}
"/$username/$action?"(controller: "user", action: "profile")
}
...
}
And of course, I'll need to do something similar in my user registration process to avoid usernames to be equal to some controller name.
That's it, thank you all.
Assuming you have a UserController and you are going to map any domain.com/username to the show action of user controller, your url mapping could be something like this :
In my example, name will become a parameter in your params.
for further details refer to here
Hope this helps.
static mappings = {
"/$name"(controller: "user", action: "show")
...
}
Given your requirements, everything after http://yourdomain.com/ can be a username or one of your other controllers, which can have undesired effects depending on which url mapping is defined first (e.g. user controller vs nonuser controller). But most likely the nonuser controller list will be the smaller list, so you should place that first and filter against it, then treat all other url mappings as user mappings.
Here is an example:
static mapping = {
"/$controller/$action?/$id?" {
constraints {
controller inList: ['nonUserController1', 'nonUserController2',...]
}
}
//this should work for /username and /username/whateveraction
"/$username/$action?"(controller: 'user')
}
Some things to note here:
you need to place the non user controller url mapping first, since everything else after http://yourdomain.com/ may be a username - correct?
second you need to place a constraint to catch all the non user controllers, while ignore the user url mappings
also you need to prevent a user from signing up with a username that matches one of your non user controllers
I am trying to secure a controller:
#Secured(['ROLE_ADMIN'])
class FooBarController {
}
I have the controller mapped in UrlMappings:
"/foo/bar"
It seems that when I try and access the controller as /foo/bar the annotations are effectively ignored.
I saw something that said I need to use the controllerAnnotations.staticRules. My first question is:
Do I need to duplicate all my rules in the static rules or is it enough to say it's secured and the filters will pick up the specific rules from the annotations?
I have another scenario where I secure a contoller with UrlMappings, the default URL path is changed but not the controller name e.g.
#Secured(['ROLE_ADMIN'])
MyApiController {
}
UrlMapping is: /api/company/1/myApi
In this case the annotation is picked up without any necessary configuration in the staticRules so I'm pretty confused by what needs to be configured where and under what circumstances.
It looks like the problem here is that the UrlMapping doesn't match the default controller mapping convention. i.e. the Config.groovy mapping is referring to the UrlMapping instead of referring to the actual controller name.
See this answer: https://stackoverflow.com/a/16737980/57357 for a fuller explanation.
IMO this is non-intuitive, but that's the way it currently works. Note that it does not work this way if you use the InterceptUrlMap style of configuration. If you use the InterceptorUrlMap style, you do refer to the UrlMappings.groovy's URL mappings.
I'm new to Grails and am trying to build a CMS with it.
I want the navigation menu to read from the database so a new page will automatically get a link in the navigation. I've been reading Grails: use controller from index.gsp and related questions, but the answers don't seem to work for me. :(
I've created a domain class named Navigation and a template called _header.
In the "Navigation/list" namespace everything works fine, but outside I can't get to the Navigation data.
I've setup url mapping like so:
class UrlMappings {
static mappings = {
"/$controller/$action?/$id?"{
constraints {
// apply constraints here
}
}
"/"(controller : "Navigation", action : "list")
"/"(view:"/index")
"500"(view:'/error')
}
}
But that doesn't seem to work. Any clues on what could be the problem?
You have two mappings for "/", your new one and the original one: "/"(view:"/index") - for starters you'll need to remove the other one.
Not sure if you are aware of this, but there is an open source CMS built in Grails called Weceem. If you need to use it as part of another Grails application, there is also a grails plug in for Weceem, so you can use it as part of your app.
It might worth looking into it before building a complete new CMS :-)
I was looking at the problem all wrong, urlmapping only made the index.gsp redirect to navigation/list. What I was looking for was the
DomainClass.findAll( String query )
propertie to use in the g:each tag
<g:each in="${Navigation.findAll('from Navigation as n where n.css=?', ['ctBoven'])}" var="oNavigation" status="i">
This allows me to read any database from any page.
Let's say we have a grails web application exposing several resources.
tags
urls
users
The application has a classical web-interface which the users interact with and some administration.
We want to expose the resources from the application to clients via a RESTful API and we don't want that part of the app to clutter up the controllers and code we already have.
So we came up with the following:
If the web interface offers host/app_path/url/[list|show|create] we want the REST API to be at /host/app_path/rest/url.
So we ended up with the following UrlMappings file:
class UrlMappings {
static mappings = {
"/$controller/$action?/$id?"{
}
/* adding new urls and listing them */
"/rest/url"{
controller = "urlRest"
action = [POST: "save", PUT: "save", GET: "list", DELETE:"error"]
}
/* accessing a single url */
"/rest/url/$id"{
controller = "urlRest"
action = [POST: "update", PUT: "update", GET: "show", DELETE: "delete"]
}
/* non-crud stuff on urls */
"/rest/url/$action?/$id?"{
controller = "urlRest"
}
"/"(view:"/index")
"500"(view:'/error')
}
}
The problem is, that this isn't exactly the most DRY thing here. It gets worse as we add more resources such as tags. They would translate to yet another three blocks of very similar code...
The non-crud functions will be things like searching with specific criterions and such...
We tried generating the mapping closures with a loop, but without success. Are we completely on the wrong track here?
I would recommend the following mapping:
"/rest/url/$id?"(resource:"urlRest")
Below is the HTTP method to action mapping that this would create for the urlRestController:
GET show
PUT update
POST save
DELETE delete
I see why you might want to map /rest/url POST to save and /rest/url/id PUT to update, but that goes against the meaning of those verbs. A PUT should be the only way to add a new url and POST the only way to update a url. Doing it the way you have laid out would work and might be the best way if your constraint is to keep your current controller code untouched. However, my guess is that you controller might already be coded to handle the default mappings just fine (update/delete give error if no id, show redirects to list if no id, etc.).