Wordpress inclusion in CodeIgniter leads to destroyed sessions - wordpress

I had to add a WordPress installation to my CodeIgniter system, so I've put it in a submap called blog and excepted that folder in my .htaccess. All good and well.
I've put the all WordPress tables together with in my CodeIgniter databases with prefix _wp.
I've now loaded the WordPress blog header file into the index.php of CodeIgniter, like so;
require('blog/wp-blog-header.php');
add_filter('site_url', 'ci_site_url', 1);
function ci_site_url() {
include(FCPATH.'/application/config/config.php');
return $config['base_url'];
}
And made a registration method in my Account controller to make an actual link to my Customers. I do this because I want to make the WordPress login/registration obsolete and solely control that from the CodeIgniter login page;
protected function register_wp($email_address = FALSE) {
if ($email_address !== FALSE) {
if (username_exists( $email_address ) == NULL) {
$password = wp_generate_password(12, TRUE);
$user_id = wp_create_user($email_address, $password, $email_address);
wp_update_user(array(
'ID' => $user_id,
'nickname' => $email_address
));
$user = new WP_User($user_id);
$user->set_role('subscriber');
$login_data = array(
'user_id' => $user_id,
'password' => $password,
);
return $login_data;
}
else {
// User already exists with that email address
return FALSE;
}
}
else {
// No email_address given
return FALSE;
}
}
And the login method, to give an idea;
protected function login_wp($user_id = FALSE) {
if ($user_id !== FALSE) {
$user_login = 'admin';
$user = get_userdatabylogin($user_login);
$user_id = $user->ID;
wp_set_current_user($user_id, $user_login);
wp_set_auth_cookie($user_id);
do_action('wp_login', $user_login);
}
else {
// No user_id given
return FALSE;
}
}
All still going well. But here comes the clash; something I was very sad about because everything worked very well up until now:
WordPress overtakes the session and kills CodeIgniter's session.
I already tried tons of things;
session_rename('PHPSESSIDWP'); and then starting another session (with another name) for CodeIgniter after WordPress was loaded
COOKIE path (I'm not 100% sure if I done this right, as it didn't change at all. Read some things online it doesn't work well in all browsers either)
COOKIE domain (seemed to have no effect)
The problem is I can't load the require('blog/wp-blog-header.php'); only in the controller method, as I need to be able to control the logged in state of the WordPress part. Besides that I will get complaints about the site_url() function, that's already claimed by the URL helper.
I think the problem is mainly because both CodeIgniter and WordPress use their own unique way of handling Sessions (CI in the Database and WordPress in "super globals") which probably only makes them use the cookie to remember a "state".
My whole CodeIgniter system already runs on the Database-driven Session models so that's an absolute no-go to make a switch. For WordPress it seems it can't even work with session anymore with it's code features (I know session "do" work, but that doesn't seem to count in any way for the WP core system).
Also I quoted out wp_unregister_GLOBALS(); in the wp-settings.php file.
Plus that I also tried to rename my session COOKIE name in CodeIgniter to use something like session_ci
I really hope someone knows a way to being able to tell CodeIgniter or WordPress to only update their values and don't kill the whole session each time. I also read something about splitting up cookies with .htaccess but can't find good resources on it. So if anyone knows how to do that, I would be eternally grateful.
I'm in despair. Finishing it for 98% and then getting such a letdown in the end :(..
Update
Maybe I can do something in the WordPress section that handles the cookies?
http://codex.wordpress.org/Function_Reference/wp_set_auth_cookie
Sadly I'm not really home in the WordPress world. I solely have to use it this one time due to the bought template that the people really wanted to use in the blog.
Also this page states the following;
WordPress uses the two cookies to bypass the password entry portion of wp-login.php. If WordPress recognizes that you have valid, non-expired cookies, you go directly to the WordPress Administration interface. If you don't have the cookies, or they're expired, or in some other way invalid (like you edited them manually for some reason), WordPress will require you to log in again, in order to obtain new cookies.
I wonder tho, how to bypass that "invalid" check, which probably is the reason it kills the CodeIgniter cookie(s)? Weirdly enough tho, it seems the session_ci value stays, although the session still seems killed.

You need to put your session start at the very top of config.php.
This is the only place a session will not be destroyed by WordPress.
if (!session_id())
session_start();
If your PHP installation does not have register_global enabled, the
above code should allow you to use session, however, if it does, you
will not be able to get the data that was set in previous request.
This is because WordPress will destroy all data contained inside
session variable when it does the initialization.
Here's why and troubleshooting on this -> kanasolution.com
EXPANDED ANSWERS:
Source: http://codex.wordpress.org/WordPress_Cookies
On login, wordpress uses the wordpress_[hash] cookie to store your
authentication details. Its use is limited to the admin console area,
/wp-admin/
After login, wordpress sets the wordpress_logged_in_[hash] cookie, which indicates when you're logged in, and who you are, for
most interface use.
So WordPress clearly dislikes the way that you're writing cookies, maybe their lack of 8 pass MD5 hash etc? WordPress encryption methods
The WordPress Environment
The next thing I would try is integrating your custom login page into the WordPress environment instead of just requiring the header. (lets stay away from editing core)
From WordPress & AJAX by Ronald Huereca page 78 explains manually loading the WordPress environment.
The use of the dirname functions depend on the hierarchy of your file. Adjust them as needed. Code should be used before the tag of your file.
$root = dirname(dirname(dirname(dirname(dirname(__FILE__)))));
if (file_exists($root.'/wp-load.php')) {
require_once($root.'/wp-load.php');
/*Run custom WordPress stuff here */
//Output header HTML, queue scripts and styles, and include BODY content
wp_enqueue_script('my_script', get_stylesheet_directory_uri() . '/my_script.js', array('jquery'), '1.0.0');
wp_print_scripts(array('my_script'));
}

Related

What's the best practice/ a secure way for saving and processing a password of a plugin's option page?

First of all, I'm not a security expert and new to form validation, password storing and wp plugin development. Any wp plugin tutorial I've been looking at never had a chapter about API passwords. Googling for wordpress password issues didn't return any satisfying results. So that's why I'm asking here.
I want to create a custom Wordpress Plugin which works with a Soap API of another page. To use the Soap API a login is needed. So the Wordpress built in functions add_option(), update_option() and get_option() are all working with plain text.
But I know that in the wp_config file authentication keys can be saved. But how to use them in an option page form to encrypt the password? And would it be possible just to store them in the database, decrypt it and use it in the backend but not showing it on the options page if the user visits that page again. So that the password field just has some black spots in it (not the same amount of the chars of the pass) and the password option only is updated if something is written into that field.
Normally the code is like this:
register_setting( 'my_plugins_option', 'my_options', 'my_validation_function' );
$options = array(
'user' = > 'name',
'password' = > 'pass',
//... other options
)
update_option( 'my_plugins_option', $options );
But how could I make this more secure? I've seen many plugin examples but nothing was about storing passwords. I'm looking for something like this:
function my_validation_function($input){
if($input['password']=='•••••'){
//use the default value of the database if nothing was changed
$old_options=get_option('my_plugins_option');
$input['password']=some_decrypting_function($old_options['password']);
}
else{
//use the password sent from the form
$password=esc_sql(some_encrypting_function($input['password']));
}
// ... validate the other inputs
update_option( 'my_plugins_soap_api_pass', $password );
}
P.S.: This code is not tested yet of course because I don't know how to work with passwords in wordpress plugins and so I wanted to ask for the best practices first.
The other question is: If the modified version of the code from above would work and the password is saved once and never loaded into the Dashboard frontend again (just showing this: '•••••' once typed in) would it be save to work with the get_option() function, decrypt the password and use it in the backend?
Here are a couple recommendations. They aren't specific to WP, but can apply.
1) Save an encrypted password in the options table (use whatever encrypting function you want, just don't write your own)
2) In the options page, simply do NOT output the password. Leave that field blank, and don't require it to be entered if there is already a password stored in the database.
3) Only decrypt the password retrieved from the options table just prior to actually needing it in code.

"An active access token must be used" (Dec 2012) && The proper way to set up Facebooks PHP-SDK for Ajax calls to get the user-id

This is giving me quite some headache. I have an page-tab-application, where DB-interaction uses the facebook-user-id to assign and save data and also to check user permissions. Until a weak ago everything was working fine, but now with the upcoming december-changes this setup doesnt work anymore:
config.php:
$facebook = new Facebook( array(
'appId' => $app_id,
'secret' => $app_secret,
'cookie' => true
));
index.php:
includes config.php and gets the signed request (not important for the question
javascript.js:
calls the read-user-status.php and handles the data
read-user-status.php:
gives json-response, includes config.php and calls the $facebook -> getUser()-function to get the uid
Even when called from the index.php directly after page-load, I sometimes get the uid and sometimes I don't. Strangly enough I usually have to wait a little until I reload the page and then it works again. But this isn't always the case. This all is just very strange to me.
EDIT: Should have mentioned that this call:
$uid = $facebook -> getUser();
if ($uid) {
try {
// Proceed knowing you have a logged in user who's authenticated.
$user_profile = $facebook -> api('/me');
} catch (FacebookApiException $e) {
error_log($e);
$uid = FALSE;
echo "EXCEPTION $e";
}
}
gives out "EXCEPTION An active access token must be used to query information about the current user".
I know there quite a lot of similar questions out there, but none of the answers were helpful to my particular (and probably to the new breaking changes relied) problem.
EDIT2: I now suppose that it is a sdk-bug (https://developers.facebook.com/bugs/238039849657148 , thanks to CBroe). Any recommendations for a work-around are of course very welcome.
EDIT 3, TEMPORARY SOLUTION
Everytime you make an ajax request, you post the token you get from the FB.getLoginStatus or FB.login and read it out in the php file and set it via $facebook -> setAccessToken. Not suitable in all circumstances (you definately need to use post), is slower and brings some security issues, but still works.
Sounds like you are affected by the bug I reported beginning of November, https://developers.facebook.com/bugs/238039849657148
They’ve confirmed it and say they’re working on a fix – but since the change is only a few days away now, they should hurry up a little …
I got this working by doing the following...
if(!$user){
$loginUrl = $facebook->getLoginUrl(array(
'scope' => 'email',
'redirect_uri' => $app_url
));
header('Location: ' . $loginUrl);
}
I also added my app to be integrated with:
Website with Facebook login
App on Facebook
Page Tab
try by adding access token to request.
$accessToken = $facebook->getAccessToken();
$user_profile = $facebook->api('/me?access_token=' . $accessToken);
I found a work-around for this, until it is fixed (which it seems like, wont be in time until the changes take place).
Everytime you make an ajax request, you post the token you get from the FB.getLoginStatus or FB.login and read it out in the php file and set it via $facebook -> setAccessToken. Not suitable in all circumstances (you definately need to use post), is slower and brings some security issues, but still works.
if it you are lucky and your version of php sdk still registers session variables than right after _graph method declaration:
//find this method below
protected function _graph ($path, $method = 'GET', $params = array ())
{
//paste right after _graph method declaration code below:
if (isset($_SESSION["fb_".$this->getAppId()."_access_token"]))
{
$this->setAccessToken($_SESSION["fb_".$this->getAppId()."_access_token"]);
}
//till here
//and you are good to go
//remember: your version of sdk must be registering access token variable in session
//right after ajax call
//i used git to get version before last commit of sdk published on github

How can I redirect users when they log into Drupal UNLESS they are placing an order?

I want to redirect users when they log in to my site to a specific page. I currently do this in hook_user:
if($op == 'login') {
drupal_goto('defaut');
}
This mostly works fine. However, I'm using Ubercart to take orders, and have it configured to automatically log in new users. This happens before the conditional action to update the order status to "Completed" is triggered. This means that when the user is logged in automatically, my hook_user redirects the user, and bypasses the remaining order processing.
At the moment, I'm working around this by checking debug_backtrace for the calling function uc_cart_checkout_complete somewhere in the call stack, but this sounds like a really dirty way to resolve it.
Can anyone suggest a cleaner way to achieve my conditional redirection without hacking great chunks of Ubercart?
You can use hooks of ubercart (for example, hook_order, hook_cart etc of ubercart, see in ubercart\docs\hooks.php ), add $_SESSION['no_redirect'] = true; there, and change you redirection:
if($op == 'login' && !$_SESSION['no_redirect']) {
unset($_SESSION['no_redirect']);
drupal_goto('defaut');
}

Drupal: access permissions for php scripts?

I'm writing a custom php code in my Drupal website. I need to load the content of specific pages from PHP.
These pages are visible only for authenticated users, and it seems I cannot access them from php, even if I trigger the script when I'm logged in as user.
Is there a way to simulate "a logged in" user from php, so I have access to all the content of the website ?
update:
global $user;
if (user_access('access content')) {
require_once("dompdf/dompdf_config.inc.php");
$html = file_get_contents('http://mywebsite.com/admin/store/orders/45/invoice/print');
$dompdf = new DOMPDF();
$dompdf->load_html($html);
//$dompdf->load_html_file('invoices/' . $file);
$dompdf->render();
$dompdf->stream("sample.pdf");
}
I've tried with relative path and it is the same...
And this is with impersonating the admin user
//access as administrator
global $user;
$original_user = $user;
session_save_session(FALSE);
$user = user_load(array('uid' => 1));
//generate pdf
require_once("dompdf/dompdf_config.inc.php");
$html = file_get_contents('http://mywebsite/admin/store/orders/45/invoice/print');
$dompdf = new DOMPDF();
$dompdf->load_html($html);
//$dompdf->load_html_file('invoices/' . $file);
$dompdf->render();
$dompdf->stream("sample.pdf");
//logout as administrator
$user = $original_user;
session_save_session(TRUE);
Still I get access denied as resulting page (and generated pdf).
thanks
The code to do so is:
<?php
if (user_access('access content')) {
print "You have the permission 'access content'";
}
?>
Running code that circumvents the permission system might seem simple and easy, but is really a serious security hole.
However, since that is what you ask:
<?php
global $user;
if ($user->uid) {
print "You are a registered user"
}
?>
But again, never use this as a replacement for permissions.
These pages are visible only for authenticated users, and it seems I cannot access them from php, even if I trigger the script when I'm logged in as user.
Drupal checks if the user has permission to view a node using the global variable $user. To do what you are trying to do, if you cannot trust that the currently logged in user have the permission to view the node you are interested in, you should read Safely Impersonating Another User.
I am not saying that you should be doing that. Before to impersonate another user, I would verify if the followed approach is the only possible one.
For example, if you just need to access a field contained in a node, then you can use node_load(), which doesn't verify if the current user can view the loaded node.
If you need to show the body of a node, you can use the following code:
$node = node_load($nid);
if ($node) {
$body = check_markup($node->body, $node->format, FALSE);
}
Showing information for which the current user doesn't have access is considered a security issue, though.
Update
The issue with your code is that you are using file_get_contents('http://mywebsite/admin/store/orders/45/invoice/print'); doing so, you are opening a new connection to the site, and the new connection is opened as anonymous user. That is the reason the node that authenticated users are able to see is not returned.
Even if the code would work, what you get is not the HTML to render the node only, but also the full page, including the blocks Drupal normally show on the top, and to the left/right sides.
If you are interested in rendering a node, then you should use the following code. (It's just a skeleton, and it's not complete.)
// $nid is the node ID.
// Check the result, in the case the node has been deleted, or there are other errors.
$node = node_load($nid);
if ($node) {
// The arguments tell the function that you don't want to render a teaser, that the node is
// rendered as it is the only node in the page, and that you don't want the additional
// links that are usually rendered after the node content.
$html = node_view($node, FALSE, TRUE, FALSE);
// This is your code.
$dompdf = new DOMPDF();
$dompdf->load_html($html);
$dompdf->render();
$dompdf->stream("sample.pdf");
}
About the updated code.
Your file_get_contents will pull in the content as "anonymous user". That is just one reason why your code is a bad idea:
Whenever your code runs, it will open your own site and parse that code: resulting in at least two "Drupals" to be loaded: effectively at least two pageviews to show one page to a user. But many more problems with this approach are possible.
Instead, you should find the code/function that creates the page at http://mywebsite.com/admin/store/orders/45/invoice/print and use that as input for your PDF-creator.

How to perform Request destination in user page

I am trying to redirect any logged user attempts to access /user.
In my module the next code to redirect after login:
function ccmm_user($op, &$edit, &$account, &$category = NULL)
{
switch($op){
case 'login':
$_REQUEST['destination'] = 'admin/';
break;
}
}
This is working. Then I try with case 'view': but it is useless.
It sounds like you want the user to never get to the /user page, whether on login or even by going there manually.
In that case you should do a simple check in a hook_init function like this:
function ccmm_init() {
if ( $_REQUEST['q'] == '/user' ) {
drupal_goto('/admin'); // Or where ever you want to send them
}
}
Of course there are a lot of checks you should do, and you may want to look into using the Global Redirect module, don't worry it's only 8k in size so the concern about adding yet another module is not such a problem in this case.
You could try the Login Destination module rather than writing your own code.
If you're not familiar with the Login Toboggan module, you should be.
A better way could be to use hook_menu_alter in your module to just remove the menu entry for /user/%user_uid_optional, or move it to another URL.
That way you won't only be handling just logins (as you currently do), but ANY access to /user/ (caveat: including those by the admin user). Or you could use the same hook to modify the access check and only grant it to users with higher permissions, like administer users

Resources