Other Stuff

UoM::RCS::Talby


Page Contents:


Page Group:

2010:

2009: 2008:


Related Pages:





Javascript, Java and Signed Applets

1.

Background

Wanted: to have a Web browser open a local file, make changes via a textarea in a HTML form, then save the updated version to local disk. Not wanted: any requirement for a server (so no CGI or similar).

Javascript could do this easily — only it can't: Javascript has no way to read/write from/to local disk for security reasons. However, Javascript can call Java applet methods which can write to local disk provided the applet is signed and/or the local Java security policy allows it. (This policy is very fine-grained, so, e.g., one can allow read/write to/from specified files only, from an applet signed by a specified author.)

2.

Javascript Security Model

From Javascript running within a Web browser, it is not possible to do local (client-side) file IO (for security reasons). One can work around this by calling methods in a Java applet (or ActiveX object).

3.

Calling Java Methods from Javascript

This is easy, for example:

  <FORM name="editform" onSubmit="document.editFile3.applyEdits('/path/file.suffix', document.editform.field.value)">
  .
  .
  .
  </FORM>

4.

Java Applet Security Model

By default, Java applets cannot write to local (client-side) disk. However, the Java applet security model provides a fine-grained permissions system which can be configured to allow applets to read/write to/from specified files, if said applets are signed by specified authors, and if the browser user choosed to accept the certificated with which the applet is signed (i.e., clicks "yes" in the popped-up window).

A bit more detail:

Signed Applets
http://www-personal.umich.edu/~lsiden/tutorials/signed-applet/signed-applet.html http://www.pawlan.com/monica/articles/signedapps/
Java Security Policy
Is defined by the contents of /usr/local/jre1.6.0_17/lib/security/java.policy. Permissions may be granted to all applets, or those signed by specified authors. Examples may be found at
  http://java.sun.com/j2se/1.3/docs/guide/security/PolicyFiles.html#Examples


As a proof-of-concept, I have this working:
  grant codeBase "file:/home/simonh/private_html/PIM/__EDITS/-" {
          permission java.io.FilePermission "${user.home}/newfile", "write";
  };
i.e., any applet with the specified codeBase — which can be determined from the Java console by hitting l — can write to /home/simonh/newfile (in my case), irrespective of who, if anyone, it is signed by.

5.

Stumbling Blocks and Trouble Shooting

There are lots of places to stumble and fall; fixing things is easy — provided one can find out what actually is the problem.

6.

Tools

A couple of sources of information on what is actually going on --- or not:

Javascript Console
In Firefox v3.0.x, at least, menu: Tools, Error Console opens up a window with the Javascript console in it — essentially, STDERR goes here.
Java Console
Again, more or less, STDERR goes here. Two ways to open it: /usr/local/jre1.<version>/bin/ControlPanel, then select the Advanced tab, then Java Console, Show Console and Apply; or get an add-on for Firefox — menu: Tools, Add-ons, Get Add-ons, enter "java console" and install.

7.

The Trouble Shooting History

A short history, in chronological order, of what went wrong and the fixes applied.

Javascript Console Error: PrivilegedActionException
On the Javascript console:
 Error: uncaught exception: java.security.PrivilegedActionException:
                            java.security.PrivilegedActionException:
                            java.lang.reflect.InvocationTargetException
But what exactly is the problem? And doesn't this look like a Java exception, not a Javascript exception?

What's happening here: a Java exception is not getting caught within the Applet and is bubbling up to the calling Javascript. This was because I had

  try {
      BufferedWriter output =  new BufferedWriter(new FileWriter("/home/simonh/newfile"));

      output.write(newItemText, 0, 100);
      .
      .
    }
  catch (IOException ex){
      .
      .
and should have had
  catch (Exception ex){
Catching the exception in Java and getting a stack trace gives us the required info.
Java Console Stack Trace: java.io.FilePermission
Once catch (IOException ex) had been changed to catch (Exception ex), a stack trace asked for. . .  
        catch (Exception ex){
            ex.printStackTrace();
          }
. . .  and javac, jar cvf... and jarsigner all rerun, we got
  • no errors in the Javascript console;
  • a nice stack trace in the Java console allowing us to figure out what exactly is the problem.
The issue was a thrown java.io.FilePermission — the applet could not write the file it wanted to; a change in the java.policy was required.
Java Security Policy File
The Java security policy is defined by /usr/local/jre1.6.0_17/lib/security/java.policy. Adding
  grant codeBase "file:/home/simonh/private_html/PIM/__EDITS/-" {
          permission java.io.FilePermission "${user.home}/newfile", "write";
  };
for example, means that any applet — signed or unsigned — which lives within file:/home/simonh/private_html/PIM/__EDITS can write to a file called /home/simonh/newfile.

8.

Annoyances, Tips and Tricks

Appendix: Complete Working Example

The Applet code

import java.applet.*;
import java.io.*;

public class editFile3 extends Applet {
    Frame f = null;
  
    public void init() { }

    public void applyEdits(String itemFile, String newItemText) {
        try {
            BufferedWriter output =  new BufferedWriter(new FileWriter("/home/simonh/newfile"));

            output.write(newItemText, 0, 100);
            output.flush();
            output.close();

            Process proc = Runtime.getRuntime().exec("/home/simonh/src/_pim/pim2.pl");
            proc.waitFor();
	        // ...ensure external process finishes before we finish, else hosting Web page
                //    will be reloaded before the html is updated...

          }
	catch (Exception ex){
	    ex.printStackTrace();
          }
      }

The HTML/Web Page

<HTML>
<HEAD><TITLE>EDIT: Netflow_Stats</TITLE></HEAD>
<BODY>
<APPLET name="editFile3" archive="editFile3.jar" code="editFile3.class" width=0 height=0>
</APPLET>
<FORM name="editform" onSubmit="document.editFile3.applyEdits('/home/simonh/private_html/PIM/__ITEMS/Netflow_Stats.html', document.editform.edits.value)">
<TEXTAREA name="edits" rows="24"  cols="80">
<MODULES>Back_Burner</MODULES>
<TITLE>Netflow_Stats</TITLE>
<MORE>Get Netflow data from Malcolm and process.</MORE>
<PEOPLE>Malcolm_Pitcher</PEOPLE>
<EPOCH>2145920461</EPOCH>
</TEXTAREA>
<INPUT type="submit" value="Apply Edits">
</FORM>
</BODY>
</HTML>

The Required Addition to the java.policy File

grant codeBase "file:/home/simonh/private_html/PIM/__ITEMS/-" {
	permission java.io.FilePermission "${user.home}/-", "write";
};

grant codeBase "file:/home/simonh/private_html/PIM/__EDITS/-" {
	permission java.io.FilePermission "${user.home}/-", "write";
};

grant codeBase "file:/home/simonh/private_html/PIM/__NEW_ITEM/-" {
	permission java.io.FilePermission "${user.home}/-", "write";
};

grant codeBase "file:/home/simonh/private_html/PIM/__ITEMS/-" {
	permission java.io.FilePermission "${user.home}/src/_pim/pim2.pl", "execute";
};

grant codeBase "file:/home/simonh/private_html/PIM/__EDITS/-" {
	permission java.io.FilePermission "${user.home}/src/_pim/pim2.pl", "execute";
};

grant codeBase "file:/home/simonh/private_html/PIM/__NEW_ITEM/-" {
	permission java.io.FilePermission "${user.home}/src/_pim/pim2.pl", "execute";
};