Groovy help... About def edit and controllers - groovy

What does def edit = {} contain by default? You see, I was following a book but it turns out to be using an older version that's why some of the code don't work. I have this piece of code:
def edit= {
def user = User.get(params.id)
if (session?.user?.id == null){
flash.message = "You have to login first before editting your stuff."
redirect(action:'login')
return
}else if(session?.user?.id != params.id) {
flash.message = "You can only edit yourself."
redirect(action:list)
return
}else{
//What should I put here?
}
}
It's already functional. If the user clicks on edit without logging in, then he's redirected to a login page. Otherwise, if he did login, then he's only allowed to edit himself. What should I put on the "else" clause? It should already should already allow the user to edit his stuff, but I don't really know how to implement what I want. :(
It would be great if someone could share the default edit snippet.
I'm a bit new to all these, so go easy on me.

If you're talking about Grails, back up your UserController and try grails generate-controller - it will give you the complete text of default actions.
I also suggest that you look through scaffolding chapter - it's a great point to start.

the default edit action should look like this (pseudo-code, it depends on the actual domain class you create the code upon):
def edit = {
redirect(action: "show", id: params.id)
return true
def <domain>Instance = <DomainClass>.get(params.id)
if (!<domain>Instance) {
flash.message = "${message(code: 'default.not.found.message', args: [message(code: '<DomainClass>.label', default: '<DomainClass>'), params.id])}"
redirect(action: "list")
}
else {
return [<domain>Instance: <domain>Instance]
}
}
btw: most of the time you don't have to do the security checks by programming these explicitly in the controller code, check out the Grails Spring Security Plugin for that purpose.

Related

Hiding _all_ remote methods in loopback model

I'm using loopback 3.
I have this lines of codes in my model's js (survey.js):
let enabledRemoteMethods = []
Survey.sharedClass.methods().forEach(function(method) {
console.log(method.name, method.isStatic)
let matchingEnabledRemoteMethod = _.find(enabledRemoteMethods, {name: method.name});
if (!matchingEnabledRemoteMethod) {
Survey.disableRemoteMethodByName(method.name, method.isStatic);
}
});
It works.... almost. I could still see in the explorer the REST endpoint for "PATCH /surveys/{id}". My expectation is: there shouldn't be any REST endpoints listed in the explorer.
Then I examined the URL corresponding to that operation, it is:
http://localhost:3000/explorer/#!/Survey/Survey_prototype_patchAttributes
Which, according to the documentation, means that patchAttributes is a static method.
Then I cross checked with the output in the console... there it says: pathAttributes is not static.
Incosistency!
I even have tried adding this line:
Survey.disableRemoteMethodByName("patchAttributes", true);
Also
Survey.disableRemoteMethodByName("patchAttributes", false);
No luck.
Can someone confirm if it's a bug in loopback 3 (I don't know about loopback 2, haven't checked)? If it's a bug I wouldn't have to spend time on it and just wait until it gets fixed. But if it's not a bug, can someone point out what's missing in my code?
Thanks!
UPDATE: figured out how
With this line I'm able to get rid of it:
Survey.disableRemoteMethodByName("prototype.patchAttributes", true);
The second parameter doesn't seem to matter (you can put false as well). Not sure why though (I suppose it should've accepted true only).
This is my current solution:
let disabledPrototypesRemoteMethods = ['patchAttributes']
let enabledRemoteMethods = [
"create", "findById", "replaceById", "deleteById",
"replaceOrCreateQuestion"
]
Survey.sharedClass.methods().forEach(function(method) {
if (enabledRemoteMethods.indexOf(method.name) == -1) {
Survey.disableRemoteMethodByName(method.name);
}
if (disabledPrototypesRemoteMethods.indexOf(method.name) > -1) {
Survey.disableRemoteMethodByName("prototype." + method.name);
}
});
Still, one small detail: this thing still shows up (I suppose it provides the POST alternative for the normal PUT for the replaceById operation..., but I don't want it; I want to force user of my API to go with the PUT only):
http://localhost:3000/explorer/#!/Survey/Survey_replaceById_post_surveys_id_replace
I tried adding this line:
Survey.disableRemoteMethodByName("replaceById_post_surveys_id_replace");
No luck.
Anyway... hope this useful for others; loopback doc is kind of sketchy.
You can get the prototype methods as well by looking at the stringName property. That way you can include the prototypes in your list.
The stringName includes the sharedClass name in the value, so you will need to parse that out.
module.exports = BusinessProfileContacted => {
const enabledRemoteMethods = ["create", "findById", "replaceById", "deleteById", "replaceOrCreateQuestion", "prototype.replaceAttributes"];
Survey.sharedClass.methods().forEach(method => {
const methodName = method.stringName.replace(/.*?(?=\.)/, '').substr(1);
if (enabledRemoteMethods.indexOf(methodName) === -1) {
Survey.disableRemoteMethodByName(methodName);
}
});
};

Orchard CMS ask for an invite token to link to backend data during registration using workflows only

I am using Orchard CMS v1.9 and want to display a custom registration page to accept the usual username/password/email and an additional token (invite token). The token will be used to match the user to some to custom data on the server.
I have walked through this blog Customizing User Registation With Dynamic Forms And Workflows. But in addition to what is achieved in this blog I want to force a registering user to enter a token. The token is used to lookup data on the server and create a link to the userpart.
Adding the token to the form is not the issue - its the querying and linking the entered token to the backend data and storing it in the userpart that im finding awkward.
Is this possible using just workflows - or do i need a custom module? I did not see a custom action that allowed me to match the token and link.
Is there a custom module already available that does something
similar?
Disclaimer: This approach is currently based on Orchard 1.10 but was initially developed on the 1.9.x branch. It does not rely on Dynamic Forms and Workflows, but I think you could achieve something similar with those modules.
Okay so I ended up building an example module with our approach to extended users / activation system. I stripped out a lot of code, but also let some juicy parts, which aren't directly related to your answer, in it.
First you should check out the UsersController it has the activate actions you are searching for. You may need to extend the orchard LogOn-View and include some GET & POST Actions accordingly.
[AllowAnonymous]
[HttpGet]
public ActionResult Activate(string activationCode)
{
// validation stuff....
var viewModel = new CustomUserActivate
{
// This is the activationCode you're looking for
ActivationCode = userFromActivationCode.ActivationCode,
UserName = userFromActivationCode.User.UserName,
WelcomeText = userFromActivationCode.WelcomeText,
Email = userFromActivationCode.User.Email
};
return this.View(viewModel);
}
[AllowAnonymous]
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Activate(CustomUserActivate input)
{
if ( input == null )
{
this.ModelState.AddModelError("_form", this.T("The argument cannot be null").Text);
}
CustomUserPart customUserPart = null;
if ( this.ModelState.IsValid )
{
customUserPart = this.myService.GetCustomUserByActivationCode(input.ActivationCode);
if ( customUserPart == null || customUserPart.User == null || customUserPart.User.UserName != input.UserName )
{
this.notifier.Add(NotifyType.Error, this.T("The activation failed"));
}
if ( string.IsNullOrEmpty(input.Email) )
{
this.ModelState.AddModelError("Email", this.T("You must specify an email address.").Text);
}
else if ( input.Email.Length >= 255 )
{
this.ModelState.AddModelError("Email", this.T("The email address you provided is too long.").Text);
}
else if ( !Regex.IsMatch(input.Email, UserPart.EmailPattern, RegexOptions.IgnoreCase) )
{
// http://haacked.com/archive/2007/08/21/i-knew-how-to-validate-an-email-address-until-i.aspx
this.ModelState.AddModelError("Email", this.T("You must specify a valid email address.").Text);
}
else if ( !this.myService.VerifyEmailUnicity(customUserPart.User.Id, input.Email) )
{
this.ModelState.AddModelError("Email", this.T("This email address is already in use.").Text);
}
}
if ( !this.ModelState.IsValid )
{
return this.View(input);
}
Debug.Assert(customUserPart != null, "customUserPart != null");
var user = customUserPart.User;
var userParams = new CreateUserParams(user.UserName, input.Password, input.Email, passwordQuestion: null, passwordAnswer: null, isApproved: true);
this.myService.ActivateCustomUser(customUserPart.Id, userParams);
this.notifier.Add(NotifyType.Information, this.T("Your account was activated. You can now log in."));
return this.RedirectToAction("LogOn", "Account", new { area = "Orchard.Users" });
}
The interesting stuff happens in MyService.cs.
We designed the activation system so that you can still leverage all the features of the Orchard.User Module like Email-Verifcation.
For this we've implemented some CustomSettings, where you can decide if your user get's completely activated when an ActivationCode is used or if you trigger the normal Orchard mechanism.
I guess it's best to checkout the module and step through the code in Visual Studio.
Here a two screenshots of our activation views.
Step 1 - Enter your activation code
Step 2 - Fill in the remaining fields
Profit!
All the additional source is to make use of the CustomUser / ActivationCode in Workflows, Events, Tokens, etc. But I leave this for you to discover.
If you want more detailed descriptions of the source on GitHub let me know.
Hope this helps!

How to get the text of /html/head/title element using Geb framework?

Using the Page Object pattern, I'd like to implement some "at" verifications based on the text of /html/head/title element.
How do I get the text of the title?
I know Geb doesn't support XPath expressions.
#Tim_Yates is right, but you specifically asked about the Page Object model.
You setup the rules for your successful page load, like so:
class GoogleHomePage extends Page {
static url = "http://google.com/"
static at = { title == "Google" } // the bit you asked about
}
Then, your actual test:
Browser.drive {
to GoogleHomePage // goes to GoogleHomePage and verifies by calling at().
}
(if you don't want at() checking, use via() instead of to().)
The documentation shows:
Browser.drive {
go "http://google.com/ncr"
// make sure we actually got to the page
assert title == "Google"
....

Grails domain objects - limit access to specific user

Is there a way to limit access to Grails domain objects across the board to the owner of the object?
For example, I can make an assert easily, but I don't want to duplicate that everywhere, or risk missing a spot.
This isn't exactly the same as multi-tenancy because it's not just a tenant ID - it may be specific business logic for different domain objects.
class MyDomain {
String name
String user
}
class MyController {
def show(Long id) {
def obj = MyDomain.get(id)
// *** How do I not do copy-paste this line in each individual controller
// that touches MyDomain?? ***
assert obj.user == CURRENT_USER
return obj
}
}
There are so many ways to handle such scenarios, as other answers are suggesting, however, I think one proper way to approach it is to use Spring Security Plugin and spring-security-acl plugin. ACL plugin will drill down into object level and helps you to control
According to doc
"The ACL plugin adds Domain Object Security support to a Grails
application that uses Spring Security."
The combination of both security core and ACL can help you accomplish what you need.
Filter might be one way, another way is to adjust your queries if possible. You would probably need to be able to search for some other criteria other than the ID. Without understanding your use case a little more, it is hard to give a better answer.
def show(String someValue) {
def currentUser = howeverYouGetYourUser
def obj = MyDomain.findByUserAndSomeValue(currentUser, someValue)
if (obj) {
// yeah!!!
} else {
// boo!!
}
}
Ideally, if you are looking for specific data for a specific user, ID isn't really the way to go.
Iam not sure if you can do it in domain level but one way is using filters. There is a filters plugin available for Grails. Keep the User in session and verify in filter for each request..
Sample filter code:
class SecurityFilters {
def filters = {
loginCheck(controller: '*', action: '*') {
before = {
if (!session.user && !actionName.equals('login')) {
redirect(action: 'login')
return false
}
}
}
}
}
and specify the filter attributes..
Here is the documentation grails.org/doc/2.2.1/ref/Plug-ins/filters.html

Why the command button is not being displayed in my emulator?

I have already added 5 cammands in a form and I want to add a sixth but It does not display the sixth?
I am posting my codes below.
public Command getOk_Lastjourney() {
if (Ok_Lastjourney == null) {
// write pre-init user code here
Ok_Lastjourney = new Command("Last Journey", Command.OK, 0);
// write post-init user code here
}
return Ok_Lastjourney;
}
public Form getFrm_planjourney() {
if (frm_planjourney == null) {
// write pre-init user code here
frm_planjourney = new Form("Plan journey", new Item[] { getTxt_From(), getTxt_To(), getCg_usertype(), getCg_userpref(), getCg_searchalgo() });
frm_planjourney.addCommand(getExt_planjourney());
frm_planjourney.addCommand(getOk_planjourney());
frm_planjourney.addCommand(getOk_planFare());
frm_planjourney.addCommand(getOk_planDistance());
frm_planjourney.addCommand(getOk_planTime());
frm_planjourney.addCommand(getOk_planRoute());
frm_planjourney.setCommandListener(this);
// write post-init user code here
System.out.println("Appending.....");
System.out.println("Append completed...");
System.out.println(frm_planjourney.size());
frm_planjourney.setItemStateListener(this);
}
return frm_planjourney;
}
Given System.out.println I assume you were debugging with emulator, right? in that case it would be really helpful to provide a screen shot showing how exactly does not display the sixth looks like.
Most likely you just got too many commands to fit to area allocated so that some of them are not shown until scrolled. There is also a chance that sixth command was reassigned to some other soft-button and you didn't notice that. Or there's something else - hard to tell with details you provided.
A general note - handling six actions with commands might be not the best choice in MIDP UI. For stuff like that, consider using lcdui List API instead. IMPLICIT kind of lists allow for more reliable and user friendly design than commands.

Resources