Wordpress Plugin

From RavenWiki
Revision as of 15:50, 13 March 2015 by jw35 (talk | contribs) (→‎Old Wordpress Plugin: Add vulnerability warning)
Jump to navigationJump to search

WPRavenAuth plugin

A comprehensive Raven plugin for Wordpress was written by Gideon Farrell and Conor Burgess in 2013, and can be found on GitHub. We would encourage you to fork and contribute to improve the codebase if you discover any problems, but it has been tested by a few people and is quite flexible. It was written because the plugin below by Daniel Hill has some serious issues.

Old Wordpress Plugin

This is a plugin to Raven which was written by Daniel Hill for a student-run Wordpress site.

It is provided 'as is' without support and may not work for everyone. Note that this must be used with a version of ucam_webauth.php later than 0.52 (and not 0.6, 0.61 or 0.62) to avoid the security vulnerability documented here.

Note that www.DEPARTMENT.cam.ac.uk must be changed below.

If you are able to support the (new) Shibboleth protocol then you may find the Shibboleth Wordpress Plugin useful.

<?php
/*
Plugin Name: Raven Logins
Description: Uses raven to authenticate users.
Author: Daniel Hill
Author URI: http://www.serenestudios.co.uk/
Version: 1.0 + subsequent changes following update to ucam_webauth
*/

/* This was written for the a student-run newspaper site so may require changes in terms of the 'categories' bit below */



// Load the Raven class
require_once ($_SERVER['DOCUMENT_ROOT'] . '/wp-content/plugins/ucam_webauth.php');



// Hook for adding admin menus
add_action('admin_menu', 'sscms_raven_add_pages');

function sscms_raven_add_pages()
{
	add_submenu_page('users.php', 'Raven Authentication', 'Raven Authentication', 8, 'sscms_raven', 'sscms_raven_admin_page');
}
add_action('lost_password','raven_disable_function');
add_action('retrieve_password', 'raven_disable_function');
add_action('password_reset', 'raven_disable_function');
add_action('register_form','raven_disable_function');
add_action('check_passwords', 'raven_check_passwords', 10, 3);
add_filter('show_password_fields','raven_show_password_fields');
add_action('wp_authenticate', 'raven_authenticate', 10, 2);
add_action('wp_logout', 'raven_logout');

add_filter('get_comment_author', 'raven_get_comment_author');


add_action('admin_head', 'sscms_raven_admin_page_process');




function sscms_raven_admin_page() {
	$kms = array();
	$as = array();
	$ms = array();
	
	if(file_exists(ABSPATH . PLUGINDIR . '/ravenkeys/keymasters.rkf'))
		$kms = unserialize(file_get_contents(ABSPATH . PLUGINDIR . '/ravenkeys/keymasters.rkf'));
	if(file_exists(ABSPATH . PLUGINDIR . '/ravenkeys/admins.rkf'))
		$as = unserialize(file_get_contents(ABSPATH . PLUGINDIR . '/ravenkeys/admins.rkf'));
	if(file_exists(ABSPATH . PLUGINDIR . '/ravenkeys/members.rkf'))
		$ms = unserialize(file_get_contents(ABSPATH . PLUGINDIR . '/ravenkeys/members.rkf'));
	
	foreach($kms as $key)
	{
		$kms_o .= $key . "\n";
	}
	foreach($as as $key)
	{
		$as_o .= $key . "\n";
	}
	foreach($ms as $key)
	{
		$ms_o .= $key . "\n";
	}
	
?>
	<div class="wrap">
	<h2>Raven</h2>
    <p>Raven authenticated users fall into three categories: keymasters can do everything; section editors can edit content that is assigned to them. Anyone else can sign in to post comments and no more.</p>
    <p>In each box, simply enter one crsid (the abc123 of abc123@cam.ac.uk) per line and hit save. To specify which sections a section editor can work with, enter a semicolon followed by a list of section after their name (for example 'abc123: news, music, theatre').</p>
    <form method="post">
	<h3>Keymasters:</h3>
	<p><textarea name="kms" rows="5" cols="70"><?php echo $kms_o ?></textarea></p>
    
    <h3>Section Editors:</h3>
	<p><textarea name="as" rows="5" cols="70"><?php echo $as_o ?></textarea></p>
    
    <p>(Available sections are: <?php 
	$categories = get_categories();
	foreach($categories as $cat)
	{
		//print_r($cat);
		echo strtolower($cat->category_nicename) . ', '; 
			
	}
	 ?>)</p>
    
    <!--<h3>Authors:</h3>
	<p><textarea name="ms" rows="5" cols="70"><?php echo $ms_o ?></textarea></p>-->
	
	<p class="submit alignleft">
		<input name="submit" type="submit" value="<?php _e('Update'); ?>" tabindex="90" />
	</p>
	</form>
    </div>
<?php
}

function sscms_raven_admin_page_process()
{
	if(isset($_POST['kms']) || isset($_POST['as']) || isset($_POST['ms']))
	{
		
		$kms_t = explode("\n", trim(trim($_POST['kms']), "\n") . "\n");
		$as_t = explode("\n", trim(trim($_POST['as']), "\n") . "\n");
		$ms_t = explode("\n", trim(trim($_POST['ms']), "\n") . "\n");
		$kms_o = '';
		$as_o = '';
		$ms_o = '';
		if(is_array($kms_t))
		{			
			$kms_o = serialize($kms_t);
		}
		if(is_array($as_t))
		{			
			$as_o = serialize($as_t);
		}
		if(is_array($ms_t))
		{
			$ms_o = serialize($ms_t);
		}
		
		
		$file_handle = fopen(ABSPATH . PLUGINDIR . '/ravenkeys/keymasters.rkf',"w+");
		chmod(ABSPATH . PLUGINDIR . '/ravenkeys/keymasters.rkf', 0777);
		fwrite($file_handle, $kms_o);
		fclose($file_handle);
		$file_handle = fopen(ABSPATH . PLUGINDIR . '/ravenkeys/admins.rkf',"w+");
		chmod(ABSPATH . PLUGINDIR . '/ravenkeys/admins.rkf', 0777);
		fwrite($file_handle, $as_o);
		fclose($file_handle);
		$file_handle = fopen(ABSPATH . PLUGINDIR . '/ravenkeys/members.rkf',"w+");
		chmod(ABSPATH . PLUGINDIR . '/ravenkeys/members.rkf', 0777);
		fwrite($file_handle, $ms_o);
		fclose($file_handle);
	}
}


		
/*
	We call phpCAS to authenticate the user at the appropriate time (the script dies there if login was unsuccessful)
	If the user has not logged in previously, we create an accout for them
*/
function raven_authenticate($username, $password) {
	global $using_cookie;
	
	if($username == 'admin')
		return;
	
	require_once(ABSPATH . '/wp-settings.php');

	// Reset values from input ($_POST and $_COOKIE)
	$username = $password = '';		
	
	global $webauth;
		
	if(!isset($webauth))
	{
		$webauth = new Ucam_Webauth(array(
			'key_dir' => ABSPATH . PLUGINDIR . '/ravenkeys',
			'cookie_key' => 'sscms_raven',
			# next line (specification of 'hostname' added on 080811 due to release of ucam_webauth.php version 0.51
			'hostname' => 'www.DEPARTMENT.cam.ac.uk',
		));
	}

	$complete = $webauth->authenticate();
	
	
	if (!$complete) { die('FAIL - ' . $webauth->status() . ':' . $webauth->msg()); exit(); }
	
	if ($webauth->success()) {
	
		
		$username = $webauth->principal();
		$email = $username . '@cam.ac.uk';
		
		require_once(ABSPATH . WPINC . '/pluggable.php');
		require_once(ABSPATH . WPINC . '/registration.php');
		if(function_exists('get_userdatabylogin') && function_exists('wp_create_user'))
		{
			$user = get_userdatabylogin($username);
		
			if ($user and $username == $user->user_login) {
				// Feed WordPress a double-MD5 hash (MD5 of value generated in check_passwords)
				$password = md5($user->user_pass);
				
			} else {
				// User is not in the WordPress database
				// they passed Raven and so are authorized
				// add them to the database (password field is arbitrary, but must be constant and should be hard to guess)
				$user_id = wp_create_user( $username, md5('Authenticated through Raven'), $email );
				
				if ( !$user_id )
					$errors['registerfail'] = sprintf(__('<strong>ERROR</strong>: The login system couldn\'t register you in the local database. Please contact the <a href="mailto:%s">webmaster</a> !'), get_option('admin_email'));
				else {
					wp_new_user_notification($user_id, $user_pass);
				}
			}
			$using_cookie = true;
			wp_setcookie($user->user_login, $password, $using_cookie);
			
			$kms = array();
			$as = array();
			$ms = array();
			
			if(file_exists(ABSPATH . PLUGINDIR . '/ravenkeys/keymasters.rkf'))
				$kms = unserialize(file_get_contents(ABSPATH . PLUGINDIR . '/ravenkeys/keymasters.rkf'));
			if(file_exists(ABSPATH . PLUGINDIR . '/ravenkeys/admins.rkf'))
				$as = unserialize(file_get_contents(ABSPATH . PLUGINDIR . '/ravenkeys/admins.rkf'));
			if(file_exists(ABSPATH . PLUGINDIR . '/ravenkeys/members.rkf'))
				$ms = unserialize(file_get_contents(ABSPATH . PLUGINDIR . '/ravenkeys/members.rkf'));
			
			
			$user->user_login = trim($user->user_login, "\n");
			$user->user_login = trim($user->user_login);
			
			$user->role = 'subscriber';			
			if(is_array($ms))
			{
				foreach($ms as $t)
				{
					$t = trim($t, "\n");
					$t = trim($t);
					$u = explode(':', $t);
					$t = $u[0];
					if($user->user_login == $t)
						$user->role = 'contributor';						
				}
			}
			if(is_array($as))
			{
				foreach($as as $t)
				{
					$t = trim($t, "\n");
					$t = trim($t);
					$u = explode(':', $t);
					$t = $u[0];
					if($user->user_login == $t)
						$user->role = 'editor';
										
				}
			}
			if(is_array($kms))
			{
				foreach($kms as $t)
				{
					$t = trim($t, "\n");
					$t = trim($t);
					$u = explode(':', $t);
					$t = $u[0];
					if($user->user_login == $t)
						$user->role = 'administrator';						
				}
			}
			
			$user->user_pass = $password;
			// User is now authorized; force WordPress to use the generated password
			
			
			
			ldapusername($user->user_login);
			
			wp_update_user( get_object_vars( $user ));
		}				
		else {
			die("Could not load user data");
		}
		
	}
	else {
		die('Could not log you in');
	}
}

function ldapusername($crsid)
{
	$nicename = $crsid;
	$nicename = str_replace('@cam.ac.uk', '', $nicename);
		
	$ds=ldap_connect("ldap.lookup.cam.ac.uk");  // must be a valid LDAP server!
	
	if ($ds) {
		$r=ldap_bind($ds);     // this is an "anonymous" bind, typically
							   // read-only access
		// Search surname entry
		$sr=ldap_search($ds, "ou=people,o=University of Cambridge,dc=cam,dc=ac,dc=uk", "mail=" . $crsid . "*"); 
		$info = ldap_get_entries($ds, $sr);
		
		
		$sname = $info[0]["sn"][0];			
		$nicename = $info[0]["displayname"][0];
		if(empty($nicename))
			$nicename = $info[0]["cn"][0];
		if(empty($nicename))
			$nicename = $crsid;
		$college = $info[0]["ou"][0];
		
		ldap_close($ds);
	
	}
	$user = get_userdatabylogin($crsid);
	
	global $wpdb;
	
	$college = str_replace('- Undergraduates', '', $college);
	$college = str_replace('- Postgraduates', '', $college);
	$college = trim($college);
	$query = "UPDATE $wpdb->users SET display_name = '" . htmlentities($nicename . ', ' . $college, ENT_QUOTES) . "', user_nicename = '" . htmlentities($nicename, ENT_QUOTES) . "' WHERE ID = '" . $user->ID . "'";
	$query = apply_filters('update_user_query', $query);
	$wpdb->query( $query );
	update_usermeta($user->ID, 'nickname', $nicename);
	update_usermeta($user->ID, 'last_name', $sname);
	if(!empty($college))
	{
		return htmlentities($nicename . ', ' . $college, ENT_QUOTES);
	}
	return htmlentities($nicename, ENT_QUOTES);
}

function raven_get_comment_author($author)
{
	global $comment;
	global $wpdb;
	if(isset($comment->user_id))
	{
		$res = $wpdb->get_results( "SELECT user_login FROM $wpdb->users WHERE ID = $comment->user_id LIMIT 1" );
		return ldapusername($res[0]->user_login);
	}
	return $author;
}


/*
	We use the provided logout method (which also destorys the client's Raven cookie)
*/
function raven_logout() {

	setcookie('Ucam-WebAuth-Session', ' ', time() - 31536000, COOKIEPATH, COOKIE_DOMAIN);
	setcookie('Ucam-WebAuth-Session', ' ', time() - 31536000, SITECOOKIEPATH, COOKIE_DOMAIN);
	
	
	session_destroy();
	
	global $current_user;
	unset($current_user);	
	
}


/*
	Should match the md5 of whatever we gave wp_create_user earlier
 */
function raven_check_passwords($username, $password1, $password2) {
	$password1 = $password2 = md5(md5('Authenticated through Raven'));
}


/*
	Don't show password fields on user profile page.
 */
function raven_show_password_fields($show_password_fields) {
	return false;
}


function raven_disable_function() {
	die('Disabled');
}


A note on redirections

I had trouble with log in redirections using WordPress 3 - the redirect_to parameter in the wp-login.php URL was getting lost through the Raven authentication. To get around this, I added the following code to the above plugin:

Before the line '$complete = $webauth->authenticate();'

if(!empty($_REQUEST['redirect_to'])){
	$_SESSION['raven_redirect_to'] = $_REQUEST['redirect_to'];
}

After function sscms_raven_admin_page_process() {

if(!empty($_SESSION['raven_redirect_to'])) { // to make only subscribers use this: && current_user_can('subscriber')) {
	$raven_redirect_to = $_SESSION['raven_redirect_to'];
	unset($_SESSION['raven_redirect_to']);
	wp_safe_redirect( $raven_redirect_to );
}

Phil Ewels, September 201