Wordpress as middleware + Angular
One day I was approached by a co-worker who asked if I wanted to be involved in building an electronic health records (EHR) system for his brother. the story is that his brother and about 5 of his friends were all using a homegrown system that was being decommissioned. Each of the doctors were paying ~$1000/month for the existing system and if we could build a replacement/clone that cash could be ours.
Although I specialize in JavaScript, I’m not really a front-end guy. I hate CSS and have no eye for design whatsoever. I opted to take care of the architecture and back-end of the new system.
Recognizing the mountain of work this project was going to be, I decided that we needed to re-invent the wheel as little as humanly possible. One of the most basic functionalities of a system that handles sensitive data is administering access rights to that data, also known as authorization.
In addition to the raw programmatic solution of controlling data, I would would also need to offer the users a way to administer their access control. I really had two problems that initially looked like one. Where could I find a single solution to both of my problems: an administration panel that also has built in authorization control?
Given that I had a long history with the blogging framework, I immediately thought of Wordpress (WP)as a possible solution. One roadblock to using WP was that my partner (rightly IMO) insisted on building the front-end using Angular. At the time, WP doesn’t really play nicely with Angular out-of-the-box.
My solution was essentially to turn WP into middleware. My partner would write his Angular app completely outside of WP. This way the two wouldn’t be coupled at all. I would then load his source into WP as private pages. Now in order for a user to access the application they would have to authenticate through WP, which solved another basic security problem — authentication.
My partner decomposed his app into routes and partial templates. the cool thing was that I could control his apps ability to access any of those templates based on the authorization of the currently authenticated user. This would free his code up from having to be aware of the current user’s authorization capabilities. The code could see a route, request the partial templates and if it turned out the user wasn’t supposed to have access to any of that route’s partials, the app would get a 404 page instead of the unauthorized partial.
OK so now you have the basic idea. Let me talk about how I actually did it. My dev stack looked something like this:
- NodeJS
- Wamp
I used Node mainly for glob. I wrote a PHP script that would go through the projects directories and glob all the HTML files. Then if the HTML file matched an entry authorization.json
{
“index.html” : [“all”],
“admin.html” : [“manager”,”tecnician”],
“dashboard.html” : [“all”],
“exam.html” : [“doctor”],
“ht-top-nav.html” : [“all”],
“shell.html” : [“all”],
“sidebar.html” : [“all”],
“create.html” : [“all”],
“details.html” : [“all”],
“patients.html” : [“all”],
“widget-header.html” : [“all”]
}
The file was pushed into the WP data base using another PHP file I’ll show later. It should be noted that each files full path is used as its post title name. For example a partial, requested via “app/admin/admin.html” will be accessible from WP as a post titled “app-admin-admin”. The reason for this is two fold: leaving the html at the end and leaving the slashes in the path will cause WP to not understand our requests for the post and thereby make them inaccessible.
Within WP, I was using two plugins User Role Editor and Restrict Content by Role (RCBR). The former allowed me to created domain-specific roles like doctor, technician, etc (which you see on the right-hand side on auhorization.json) . The latter does exactly what it sounds like.
Under the hood, RCBR worked by making WP pay attention to a post metadat field it created, named _mkdo_rcbr_roles. Here’s the script I used to push the Angular app’s files into the WP database with this PHP cli script:
<?php//php push_doc.php “relPath” “authorizations”< “full path”
require_once( dirname(dirname( __FILE__ )) . ‘/wp-load.php’ );
require_once(dirname(dirname( __FILE__ )) . ‘/wp-admin/includes/admin.php’);$post = array();
//$post_id = wp_insert_post();
$contents = “”;
while($line = fgets(STDIN)){
$contents .= $line;
}//don't do HTML tag sanitization
//with custom shortcode I created
$post[‘post_content’] = ‘[raw]’.$contents.’[/raw]’;
$post[‘post_title’] = str_replace(DIRECTORY_SEPARATOR, “-”, $argv[1]);
$post[‘ID’] = post_exists($post[‘post_title’]);
$post[‘guid’] = $post[‘post_title’];
$post[‘post_name’] = $post[‘post_title’];
$post[‘post_type’] = ‘page’;
$post[‘post_status’] = ‘private’;
$auths = explode(‘,’,$argv[2]);
$auths[] = ‘administrator’;
$all = array(‘administrator’,’doctor’,’manager’,’technician’);//set authorization meta on post
//wp is serializing meta on its end
$post[‘meta_input’] = array(‘_mkdo_rcbr_roles’ => $auths);$post[‘post_author’] = 1;if(array_search(‘public’, $auths) !== false){
$post[‘post_status’] = ‘publish’;
unset($post[‘meta_input’]);}else if(array_search(‘all’, $auths) !== false){
$post[‘meta_input’] = array(‘_mkdo_rcbr_roles’ => $all);
}$action = ($post[‘ID’] == 0) ? ‘inserted’ : ‘updated’;$id = wp_insert_post( $post, true, false );//script output
if(!is_wp_error( $id )){
echo sprintf(‘%s %s with id %d. Authorized for %s’, $post[‘post_title’],$action, $id, $argv[2]);
}else{
echo sprintf(‘Failed %s %s with error “%s”’,$post[‘post_title’], $action, $id->get_error_message() );
}
?>
After the above bit of magic, we should now have our Angular app’s files stored in WP like so:
and our authorizations are stored like so
As noted above the directory separators were replaced with dashes before going into the WP database. But remember our Angular app is going to hit our server asking for a document with a url like this “app/admin/admin.html”. Without some help this request will fail to find the document. Remember the content of admin.html now exists in our authorization-protected WP pages
The last step is to use mod_rewrite to transform these bad paths into paths to valid our WP pages. Here’s what that looks like:
<IfModule mod_rewrite.c>
RewriteEngine On#on input to the DB, we’ll transofrm paths into dash-separated lists
#which become the permalink to the html content
#client code will still use path format, so we transform the request
#to match the permalink
#http://stackoverflow.com/questions/29023298/replace-all-forward-slashes-with-dashesRewriteBase /src/ehr/RewriteRule ^([^/]+)/([^/]+)(?:\.html|/)?$ $1-$2 [NE,L,R=302]RewriteRule ^(.*)\.html$ $1 [R=301,L]RewriteRule ^([^/]+)/(.+)$ $1-$2</IfModule><IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /src/ehr/
RewriteRule ^(.*)\.html$ $1 [R=301,L]
</IfModule># BEGIN WordPress
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /src/ehr/RewriteRule ^index\.php$ — [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /src/ehr/index.php [L]</IfModule># END WordPress
The first section replaces the slashes with dashes. The second section removes the “.html” from the end.
And that’s about it. Like all things in our profession there are other (probably better) ways to have done this. But there’s something fun about coaxing a piece of software into a new, unintended use. I hope you enjoyed this post!