The other day I wrote about my failure to realize that CSJS and SSJS libraries shalt not cross during my attempt to write a single library for CSJS and SSJS validation code.
Not to be out done and with some great suggestions from the wonderful Sven Hasselbach I trucked on into the next attempt.
Parameterized development
I first met this concept a few years ago. I was working on an application created by John McCann, and all the script routines for a suite of applications were sorted in notes documents. The application would look up the code it needed and then executed it as needed. This made for extremely efficient code management and the ability to make updates to code snippets without changing any “code”.
Using this design pattern I set out to create a simple document in a view with a simple JavaScript function within it – the “are the fields the same” code I used in the previous article.
I then created a Java Bean to access the notes view and get the field value.
(Yes I could have done it in SSJS – but hey this is a learning exercise as much as anything and the more Java code crosses my path (as rare as it is) the more likely I am to get familiar with it)
The bean is pretty simple and I am not going to detail the how’s and why’s and when’s of the bean – if you want to learn more about beans go see Russ Maher !!
package com.xomino.jsLookup; //load import for Domino classes import java.io.Serializable; import lotus.domino.*; import lotus.domino.local.Database; import lotus.domino.local.Document; //import for JavaServer Faces classes import javax.faces.context.FacesContext; //var marky = com.xomino.jsLookup.getValidationCode.getCode public class getValidationCode implements Serializable{ /** * */ private static final long serialVersionUID = 1L; private String theCode; public getValidationCode() { System.out.println("Starting Bean"); } // open method, this actually runs the whole App public String getCode(){ System.out.println("MARKY SSJS"); // let's add a try catch here, to grab errors near the end try { // Declare Variables, one to hold Documents/ and page through results with the other Document doc; //BEGIN DEBUG Database database = (Database) FacesContext.getCurrentInstance().getApplication() .getVariableResolver().resolveVariable(FacesContext.getCurrentInstance(), "database"); System.out.println("Database Obtained..." + database); // Find the view in question View view = database.getView("jsValidation"); System.out.println("View Obtained..." + view); //get the document doc = (Document) view.getFirstDocument(); System.out.println("Doc Obtained..." + doc); System.out.println("Loading to Xpage..."); // process the document in the View //recycle to free up mem theCode = doc.getItemValueString("js"); System.out.println("Loading to Xpage..."); doc.recycle(); } catch (Exception e) { e.printStackTrace(); System.out.println("FAIL"); } return theCode; } }
Big thanks to the Java Guru known as David Leedy for pointing out my inability to watch notesin9 videos correctly and learn how to do beans
Anyway the interesting code
The code which is stored in the notes document is just a simple JavaScript function
function checkPasswords(){ if (getComponent("Password").getSubmittedValue() != getComponent("ConfirmPassword").getSubmittedValue()){ return false; } else { return true; } }
Back in my XPage I set up two sections for code – one for SSJS and one for CSJS
The CSJS code which will run on the web page looks like this (return lookupCode.getCode())
<xp:this.resources> <xp:script clientSide="true"> <xp:this.contents> <![CDATA[${javascript: return lookupCode.getCode() }]]> </xp:this.contents> </xp:script> <xp:script src="/libValidation.js" clientSide="true"></xp:script> </xp:this.resources>
In this context the text string returned from the bean feeds directly into the contents of the script library – looks like this in the page source:
The keen eyed among you will have noticed I also included the libValidation.js function which I created in the last article which is the CSJS equivalent of the getComponent(x).getSubmittedValue()
The SSJS code is similar but with one crucial difference:
<xp:validateExpression message="Passwords Must Match"><!-- (2) --> <xp:this.expression> <![CDATA[#{javascript: var theCode = lookupCode.getCode() eval(theCode) return checkPasswords() }]]> </xp:this.expression> </xp:validateExpression>
In the SSJS I have to use “eval” to evaluate the text string which is returned from the bean. This works just fine – but eval is very very evil and I do not like this approach at all. But right now we are talking proof of concept so I am ok with it in development but would never use this in production. If the contents of the lookup document were compromised this would expose a massive hole in the security of the entire server – but that is a blog post for another day……
But the same code (lookupCode.getCode()) was used to get the code and if you notice checkPasswords() is then called in this context to test if the fields are the same.
Running this through a browser (with no CSJS yet) we can see a successful test
Then the CSJS is called from the onClick event of the submit button
<xp:button value="Submit" id="button1"> <xp:eventHandler event="onclick" submit="true" refreshMode="complete" immediate="false" save="true"> <xp:this.script> <![CDATA[ if (!checkPasswords()){ alert('passwords must match') return false } ]]> </xp:this.script> </xp:eventHandler> </xp:button>
Which looks like this
So why are we doing this again Marky?
Well the point is that CSJS alone is a better experience for the user but it is not secure – so if we can write the validation code once it can be used Client Side and Server Side with the minimum of duplication
Here is how I break the CSJS validation and bypass it – see in firefox – I can override the checkPasswords function to show a different prompt and then return true – submitting the form
Which is then validated using the SSJS – securing the application 🙂
Summary
In both cases we were able to use the checkPasswords() function which was written once and stored in the notes document So as far as I know this is the first example of using the same JavaScript code to validate a form client-side and server-side – which was the initial goal – there is some improvement to be done here though I am sure
Caveats
And there are a few…..
- For the uninitiated the eval function is evil and should not be used in production code unless you can absolutely guarantee the security of the source.
- Looking up the code in a document has an overhead and this would not scale well over many functions and many documents
- There is a lot more code written to save copying and pasting a few lines of code in this case – this did not make my life in any way shape or form easier
- There has to be a better way – and the quest has only just begun
Just a couple of observations:
1.) While eval can be considered bad, it has it’s uses, and in this case, you should be able to trust the data, it’s your data, after all. If it is compromised, you have other issues.
2.) Doing a document lookup costs basically nothing, and a single document won’t add more than a couple of ms to your response time. If you load data from several documents to build a single function, it is simple to cache the resulting text somewhere.
3.) I suspect not 🙂 But by taking care, and writing a solid backing “framework”, you could, as you say, save some copying. Sometimes it’s fun to make something, just to see if it will work, best case you have something to use, worst case you have learned something.
4.) By creating a loader.js function of sorts, you can inject your script adding new script-tags to the html DOM, this way you can at least avoid eval (but this way your script pages need to return only the script itself). A custom servlet can do this (with few lines of code). I don’t do much SSJS, so I can’t really help with that.
thank you for you feedback – I really appreciate you taking the time to write 🙂
1) true – but people need to be aware of the potential issues
2) agreed if this was to scale I would load all the functions in one lookup – not lots
3) that is exactly that this is about – doing it just for the education and fun of doing it – anything concrete will be a bonus at this point
4) the eval is on the SSJS side not HTML DOM so it won’t help in this case – nice idea though
again ! thank you 🙂
I see i mixed up the 2, CSJS & SSJS 🙂
The potential of trusting Eval on the server could of course be more dangerous than on the client, as it could give access to your server, so yes, be careful, people.
Trivial though, to compute a salted hash of the contents, and verify it before you eval it, I think…
However I do feel the “lure” of the single codebase for field evaluation, and I also have the same need in my application, but it is not an xPage app per se, so I cannot use this technique. If you wonder, it’s (a part of) an application where users can create forms via an html editor, add these forms to 1 or more CMS pages, and then have other users submit data to a Domino database. Easy enough to add field validation to the client side, but harder to do right on both server and client with the same data. I suspect the solution is to store the validation metadata (field, type, message), and use different codepaths to generate the validators. It’s another item on my Todo list 🙂
Gilgamesjh – a salted hash would work if your users don’t have access to the database through the client. Remember if you give me client access to your database (at any level of access) I can read the design – even if you ‘hide design’, its trivial to get around. If they do have access in the client then you have to assume your code is open and so any potential attacker would have access to the algorithm you use to generate the hash and thus could reverse engineer it. In that case I think your best bet would probably be to use public key encryption and have the public key in the design of the db and the private one stored separately to generate the hashes… I am facing this problem at the moment, if you can think of any other ideas I would love to know!
[…] eval() is used judiciously and in a well controlled manner is extremely powerful and allows developers the ability to do things otherwise impossible. […]
Hi there, You have done an incredible employment. I will surely yahoo it plus in my personal suggest so that you can friends and neighbors. We are confident they shall be took advantage of this web site.