CakePHP Security component

September 6th, 2011

Lately I added CakePHP’s Security component to a controller. Using the example from the cakephp cookbook caused the $_POST array to be null. Turns out the Security component does not like hidden input fields and will null all the input fields you set validatePost = false.

My controller now looks like:

1
2
3
4
5
6
7
8
9
10
11
12
    var $components = array('Security');
    function beforeFilter() {
        parent::beforeFilter();
        $this->Security->blackHoleCallback = 'forceSSL';
        $this->Security->requireSecure('index', 'view');
        $this->Security->validatePost = false;
    }
 
    function forceSSL() {
        $protocol = 'https://';
        $this->redirect($protocol . env('SERVER_NAME') . $this->here);
    }

Accessing CRX with Python

February 2nd, 2011

As a complement to my previous post about accessing CRX with Ruby, here’s the equivalent code in Python (or rather Jython). Just remember to add the jackrabbit jar file to the classpath.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import java.lang.String as String
import javax.jcr.Repository as Repository
import javax.jcr.SimpleCredentials as SimpleCredentials
import org.apache.jackrabbit.rmi.client.ClientRepositoryFactory
import org.apache.jackrabbit.commons.JcrUtils as JcrUtils
 
def listChildren (indent, node):
  print indent+ node.getPath()
  for n in node.getNodes():
    listChildren(indent+"  ",n)
 
# jackrabbit
#repository = JcrUtils.getRepository("http://localhost:8080/rmi")
#creds = SimpleCredentials("user", String("pass").toCharArray())
# crx
repository = JcrUtils.getRepository("http://localhost:4502/crx/server")
#repository = JcrUtils.getRepository("rmi://localhost:4502/crx")
creds = SimpleCredentials("admin", String("admin").toCharArray())
 
session = repository.login(creds)
name = repository.getDescriptor(Repository.REP_NAME_DESC);
user = session.getUserID()
print "logged in as " + user + " in " + name
 
root = session.getRootNode()
print  "root property = " + root.getProperty("jcr:primaryType").getString()
listChildren("", root)
 
session.logout()

Python is nearly as concise as Ruby but it lacks the iterator construct and relies on a “for n in [set]” construct. But does show the versatility of the JVM to support different languages. Dynamically typed languages offer shorter development time than statically typed ones.

Accessing CRX with Ruby

February 2nd, 2011

Occasionally I make an attempt to learn Ruby. The language has a nicely orthogonal OOP implementation but it’s not one of the C-like languages I know so the learning curve is steep. I thought I’d try using Ruby to access our new CMS.

Our web content management system (CMS) uses a data storage system called Content Repository Extreme (CRX) by Day (now Adobe). It is a hierarchical data structure based on the Java Content Repository (JCR) API. A reference implementation is Apache Jackrabbit.

Content is typically created via a web based interface. Custom functionality is provided via JSP/scriptlets. However the repository can also be accessed by standalone applications. Since access is defined by a Java API, one must program in Java or a JVM-based language. JRuby falls into the latter camp.

This article describes how to access Jackrabbit using JRuby. It describes such setup work as obtaining the jackrabbit jar file and copying it to the JRuby libs directory. It also explains line 2 where the Java String class is renamed to JString in Ruby to avoid a name collision. While this article describes how to access CRX with Java. The http access method is slower than the RMI interface but I couldn’t get the latter to work with JRuby. Probably because I am missing some jar file. Translating the Java into Ruby gives the following program which lists all the nodes in the repository by their path name.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
require 'java'
include_class('java.lang.String') {|package,name| "J#{name}" }
include_class 'javax.jcr.Repository'
include_class 'javax.jcr.SimpleCredentials'
include_class 'org.apache.jackrabbit.rmi.client.ClientRepositoryFactory'
include_class 'org.apache.jackrabbit.commons.JcrUtils'
 
def listChildren (indent, node)
  puts indent+ node.getPath
  node.getNodes.each do |n| listChildren indent+"  ",n end
end
# jackrabbit
#repository = JcrUtils.getRepository("http://localhost:8080/rmi")
#creds = SimpleCredentials.new("user", JString.new("pass").toCharArray)
# crx
repository = JcrUtils.getRepository("http://localhost:4502/crx/server")
#repository = JcrUtils.getRepository("rmi://localhost:1099/crx")
creds = SimpleCredentials.new("admin", JString.new("admin").toCharArray)
 
session = repository.login(creds)
name = repository.getDescriptor(Repository::REP_NAME_DESC);
user = session.getUserID
puts "logged in as " + user + " in " + name
 
root = session.getRootNode
puts "root property = " + root.getProperty("jcr:primaryType").getString
listChildren("", root)
 
session.logout

The first few lines of output look like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
logged in as administrator in CRX
root property = rep:root
/
  /rep:policy
    /rep:policy/allow
    /rep:policy/allow0
  /jcr:system
    /jcr:system/jcr:versionStorage
      /jcr:system/jcr:versionStorage/1b
        /jcr:system/jcr:versionStorage/1b/70
          /jcr:system/jcr:versionStorage/1b/70/dd
            /jcr:system/jcr:versionStorage/1b/70/dd/1b70dd8a-f933-4d62-b6b4-b5ab1aaef8f9
              /jcr:system/jcr:versionStorage/1b/70/dd/1b70dd8a-f933-4d62-b6b4-b5ab1aaef8f9/jcr:versionLabels
              /jcr:system/jcr:versionStorage/1b/70/dd/1b70dd8a-f933-4d62-b6b4-b5ab1aaef8f9/jcr:rootVersion

Here’s the Java version:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
package jackrabbit;
 
import javax.jcr.*;
import java.net.MalformedURLException;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import javax.naming.NamingException;
import org.apache.jackrabbit.commons.JcrUtils;
import org.apache.jackrabbit.rmi.client.ClientRepositoryFactory;
import org.apache.jackrabbit.jcr2spi.config.RepositoryConfig;
import org.apache.jackrabbit.webdav.jcr.nodetype.NodeTypeConstants;
import org.apache.commons.httpclient.HttpMethod;
import org.apache.commons.codec.DecoderException;
import org.apache.commons.logging.LogFactory;
 
public class Main {
 
    public static void main(String[] args) throws ClassCastException, MalformedURLException, RemoteException, NotBoundException, LoginException, NoSuchWorkspaceException, RepositoryException, NamingException {
        //RMI Connection
        Repository repository =
            // WebDav remoting access to CRX server (port 7402?)
            //JcrUtils.getRepository("http://localhost:4502/crx/server");
        // RMI remoting access to a CRX server (with RMI enabled) 
             //JcrUtils.getRepository("http://localhost:8080/rmi");
        // RMI remoting access to a CRX server (with RMI enabled)
        JcrUtils.getRepository("rmi://localhost:1099/crx");
        //Workspace Login
        SimpleCredentials creds =
                // crx
                new SimpleCredentials("admin", "admin".toCharArray());
                // jackrabbit takes anything
                //new SimpleCredentials("user", "pass".toCharArray());
        Session session = null;
        session = repository.login(creds);
        String user = session.getUserID();
        //List Children
        System.out.println("User: "+user+ ", Workspace: " + session.getWorkspace().getName() + "\n");
        listChildren("", session.getRootNode());
    }
 
    private static void listChildren(String indent, Node node) throws RepositoryException {
        System.out.println(indent + node.getPath());
        NodeIterator ni = node.getNodes();
        while (ni.hasNext()) {
            listChildren(indent + "  ", ni.nextNode());
        }
    }
}

Compared with Java the Ruby is somewhat more concise. The iterator syntax is much cleaner. There’s not need to write a while-loop with method calls to hasNext or nextNode. Running a scripting language under a JVM facilitates access to Java-based legacy data. I could see using such a standalone application to produce custom reports or making a global modification.

Burned by CakePHP (again)

January 18th, 2011

Over the weekend we had a php application that was scheduled to close at midnight. Instead about 7pm the complaints started to roll in that the application had closed early. The application used the php function strtotime to determine when to close. If past the deadline then the application redirected to a message page.

1
2
3
4
        if (strtotime("2011-01-15 11:59PM") < strtotime("now")){
            $this->redirect(array('controller' => 'applications', 'action' => 'deadline'));
            return;
        }

With the UNIX command line “date” I checked the date and time zone on the server. It was correct. I ran the following PHP script on the server to check PHP’s configuration (both under command line and under apache). It too reported the correct time and the zone as American/New York.

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
$a = strtotime("2011-01-15 11:59PM")."\n";
$t = strtotime("now")."\n";
echo " fix:$a\n";
echo " now:$t\n";
echo "date fix:".date("c",$a)."\n";
echo "date now:".date("c",$t)."\n";
 
if (strtotime("2011-01-15 11:59PM") < strtotime("now"))
   echo "1/15 11:59PM less than now\n";
if (strtotime("2011-01-15 11:59PM") > strtotime("now"))
   echo "1/15 11:59PM GREATER than now\n";
echo "zone:".date_default_timezone_get()."\n";

However the application was running under the CakePHP framework. So I wrote a dummy controller function and put the above code in a dummy view. In this context the script reported the time zone as “UTC” and the time as five hours earlier! Turns out CakePHP explicitly sets the time zone to UTC. So it doesn’t matter what the time zone setting is on the server or in the php.ini configuration file. I certainly did not expect that.

Did I test the code? Yes but I ran it at 4pm. The difference between UTC and EST is five hours so they were both on the same date at the time of the test.

This was the second time CakePHP behaved unexpectedly in as many months. A couple months ago we switched databases from MySQL to SQL Server. At first it appeared that CakePHP seamlessly dealt with the transition. Then we got a complaint from a client that an order was missing.

The application had a option to upload a Word document as a file and this file was stored in the database. If the file is larger than a certain size then this operation silently fails. CakePHP does not report an error. In the php.ini file the config parameters default to only 4096. Large enough to pass a simple test but too small for many files. Fortunately someone had posted about this in the comments for the mssql_connect function.

1
2
3
4
5
; Valid range 0 - 2147483647.  Default = 4096.
mssql.textlimit = 4096
 
; Valid range 0 - 2147483647.  Default = 4096.
mssql.textsize = 4096

Twice in two months CakePHP has provided unexpected results. Once by resetting the time zone and in another for silently failing a database operation. Convention over configuration can save development time but production problems are even more costly. I’m left wondering how many other unexpected items are lurking.

CodeIgniter partial,nested views for common headers/footers

December 15th, 2010

Unlike the CakePHP framework, CodeIgniter has no direct support for common headers/footers. There are a few suggests in their wiki but they have disadvantages. Compared to extending the Controller class or a separate template library; nested/partial views has the advantage of simplicity by using built-in facilities.

Here’s a small example I use for illustration.
flowershop In the Flowershop one is presented with a form and selects a color. The application responds with a list of flowers of that color. A single, common view is used for every page. This common view is handed a page title, navigation view and content view.

Here’s the controller method. The view loaded is “common/default”. In the array passed to the view are the title, a page navigation view and a page content view.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
    function flowersByColor() {
        $this->load->model('colorsModel');
        $this->load->model('flowersModel');
        $colors = $this->colorsModel->listAllColors();
        $flowers = null;
        if (isset($_POST['color_id'])) {
            $flowers = $this->flowersModel->getFlowersByColorId($_POST['color_id']);
        }
        $this->load->view("common/default", // common view for every page
            array(
                'title'=>'Flowers by Color', // page title
                'navigation'=>'flowers/nav', // page navigation view
                'content'=>"flowers/flowersbycolor", // page content view
                'colors'=>$colors, // list of colors
                'flowers'=>$flowers));  // list of flowers
    }

Here’s the view common/default.php. It sets the page title, loads a page navigation view and the page content view.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php echo doctype('html4-trans')."\n"; ?>
<html>
    <head>
        <title><?php echo $title; ?></title>
        <?php echo link_tag('css/style.css')."\n";?>
    </head>
    <body>
        <?php $this->load->view("common/banner"); ?>
        <?php $this->load->view($navigation); // page navigation?>
        <div id="content">
        <?php $this->load->view($content); // page content ?>
        </div>
    </body>
</html>

Here is view flowers/flowersbycolor.php. It is the page content view. It displays a form to input a color and if a list of flowers is present, it loads the view flowers/tableView.php that displays the list. Note that when this view is loaded, one need not re-specify the $flowers variable for the page content.

1
2
3
4
5
6
7
8
9
10
11
12
13
<h2>Flowers by color</h2>
<?php
echo form_open('flowers/flowersbycolor');
//convert array of objects to associative array
foreach ($colors as $color) $a[$color->color_id] = $color->color;
echo form_label('Color: ', 'color_id').form_dropdown('color_id', $a)."<br/>\n";
echo form_submit("submit", "Submit");
echo form_close();
 
if ($flowers) {
    $this->load->view("flowers/tableView");
}
?>

Here is the view flowers/tableView.php. It displays a table of flowers. The view is shared by other controllers.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<table border="1">
    <tr>
        <th>Edit</th>
        <th>Common Name</th>
        <th>Color</th>
        <th>Season</th>
        <th>Description</th>
    </tr>
    <?php  foreach ($flowers as $flower?>
    <tr>
        <td><a href="edit/<?php echo $flower->flower_id; ?>">edit</a>
            <a href="view/<?php echo $flower->flower_id; ?>">view</a>
            <a href="delete/<?php echo $flower->flower_id; ?>">delete</a>
        </td>
        <td><?php echo $flower->commonname; ?></td>
        <td><?php echo $flower->color; ?></td>
        <td><?php echo $flower->season; ?></td>
        <td><?php echo $flower->description; ?></td>
    </tr>
    <?php endforeach; ?>
</table>

Nested, partial views can be used to create a template for pages with common headers and footers without resorting to extending controllers or separate libraries.