I have a whole site contained in a wordpress plugin. I'v been trying to get away from wordpress but I still depend on it to manager users. I need to access the data of the currently logged in user, specifically their ID.
When trying to access their data in certain places I kept getting the no user object:
object(WP_User)#1033 (7) {
["data"]=>
object(stdClass)#1032 (0) {
}
["ID"]=>
int(0)
....
In the end, it seems to boil down to this. I'm trying to get this data in controllers called by my slim router:
$this->get( '/assignments', function(Request $req, Response $res, array $args ){
require_once $_SERVER['DOCUMENT_ROOT'] . '/wp-load.php';
$ch_user = wp_get_current_user();
var_dump($ch_user);die();
}
and:
$this->post( '/userdata', function(Request $req, Response $res, array $args ){
require_once $_SERVER['DOCUMENT_ROOT'] . '/wp-load.php';
$ch_user = wp_get_current_user();
var_dump($ch_user);die();
}
Both of these routes are in the same group, lines apart from each other.
The first one returns the correctly populated user object, and the second one returns the empty one with ID:0. The only difference between the two is that one is a get request, and one is a post request.
I am no word press expert, and I am just completely baffled by this.
edit:
I found similar issues left unresolved:
WordPress Ajax Call -- WordPress User ID
Wordpress REST API Retrurning Currnet User ID as 0
From your comment I am assuming that the current user in your WordPress session was set using the standard WordPress login, e.g. http://aaa.bbb.ccc/wp-login.php.
In this case the current user credentials are received as cookies. The usual credential cookie is in $_COOKIE[LOGGED_IN_COOKIE]. Can you verify that this is identical in both your endpoints by:
var_dump( $_COOKIE[LOGGED_IN_COOKIE] )
One possibility is that this cookie is not received or damaged then the current user will be set to 0.
Related
i want to set a cookie if a specified form (Contact Form 7) was sent.
If is write the code into the init-call it works:
function process_post() {
if( isset( $_POST['kampagne'] ) ) {
setcookie('cid', get_option( 'cf7-counter', 0) + 1 , time()+3600, '/');
}
}
add_action( 'init', 'process_post' );
But that's not good, because the value of cookie is an id - stored in database, which i must get in the moment where the form will send.
So I want to set the cookie in the "wpcf7_before_send_mail" hook. But within that the cookie will not generate.
function wpcf7_do_something($WPCF7_ContactForm)
{
$submission = WPCF7_Submission :: get_instance();
if ( $submission ){
$posted_data = $submission->get_posted_data();
if ( empty( $posted_data ) ){ return; }
$changed_name = get_option( 'cf7-counter', 0) + 1;
update_option('cf7-counter', $changed_name);
$mail = $WPCF7_ContactForm->prop( 'mail' );
$new_mail = str_replace( '[cf7-counter]', '00'. $changed_name .'', $mail );
$WPCF7_ContactForm->set_properties( array( 'mail' => $new_mail ) );
return $WPCF7_ContactForm;
}
}
add_action("wpcf7_before_send_mail", "wpcf7_do_something");
I hope you can help me.
Thank you very much!
Best regards.
But that's not good, because the value of cookie is an id - stored in database, which i must get in the moment where the form will send.
So what you need is the ID that will be stored into the database when creating the cookie.
Create it
if( isset( $_POST['kampagne'] ) ) {
$cookie = 'cid';
$id = uniqid("cid:", true);
setcookie($cookie, $id, $_SERVER['REQUEST_TIME'] + HOUR_IN_SECONDS, '/', ini_get('session.cookie_domain'), (bool)ini_get('session.cookie_secure'), (bool)ini_get('session.cookie_httponly'));
$_COOKIE[$cookie] = $id;
}
; and store it
$cookie = 'cid';
$WPCF7_ContactForm->set_properties(
[
'mail' => $new_mail,
'cid' => $_COOKIE[$cookie],
]
);
.
The contact form transaction now has the id that is in the cookie.
It is independent to get_option( 'cf7-counter', 0) by binding the submission to its own identifier - like the cookie is not the cf7-counter option.
Take note on explicitly setting $_COOKIE[$cookie] = $id;, always. This is to have the ID in the request itself.
Take double note on this action, as the code does set the cookie with a new ID even if it was already set. If that is not your intention, check if the cookie is unset beforehand (later references on setting a cookie in Wordpress have it, too).
It is now more eventually consistent, up to the level of uniqueness of the uniquid() function (study it if you want to make use of it), which is an improvement over the situation where the chance of being inconsistent was much higher (that is when using the cf7-counter option).
An insecure random-unique ID normally is enough for short term transaction tagging, e.g. having it for an hour in the cookie.
Which is something you may want to put more of your attention to. And the example is less Wordpress specific, there is more to get from in Wordpress like nonces, keys, salts, cookie-paths, user-level cookies etc. pp. to adhere to that system specifically.
It requires you to study Wordpress first, which I can't take of your shoulders, and there is even more to learn first, like how cookies work. Perhaps ask the more general questions first that you have to close such gaps.
Another answer I was thinking about first is to take a more fitting action so that you have the id the moment you can do both: setting the cookie and storing to the database.
As it has been commented, such a solution would need to find the right place in time. But that is merely how cookies work.
(And not specifically Wordpress.)
As you have already seen yourself, the action you wanted to use did not work.
And even if you would find such another action, this is still Wordpress, and it would be unpredictable that it would suffice: The cookie might be set in your tests, but not when a user visits the site (or any site that is out there with Contact Form 7 which this answer should answer if it is worth it).
To set a cookie in Wordpress, there is no free choice of the action to do it in, but it first of all needs to set the cookie for which the action is invariant:
How to set a cookie in Wordpress
Setting a redirect cookie in wordpress
As the action is set in stone to set a cookie, this goes back to the answer above.
However this is not Wordpress SE, but Stackoverflow so I've put the focus more on the PHP code itself where it relates to the PHP configuration and only HOUR_IN_SECONDS is a constant from Wordpress in it. See as well my comment on it about more specific Wordpress features you may want to add to it.
It hopefully answers your question to the level you need it.
I have adopted a CakePhp 2 project. We get to the project from another project, linking to the CakePhp project with a "token" and a conference ID as a parameter in the URL. Using that token, we authorize the user, and using the conference ID get the information from the database. The session value "auth" is set to true.
We have it running on 2 "platforms" locally on my system using a vagrant machine, and on a production server. Locally the session value dies really quick and at random times. On the production server not as often, but the issues we have where Ajax calls don't seem to do what are expected, we believe are being caused by a similar issue. We have many different projects, all Laravel, with zero issues where the session values clear. This issue is strictly with the CakePhp project.
All the authentication magic happens in the beforeFilter method. The code:
public function beforeFilter() {
$session = new CakeSession();
/**
*
* We will check if the current user is authorized here!
*
*/
// If the visitor is coming for the first time, there should be a parameter in
// the URL that is the auth code to check against the database.
if ( ( isset($_GET['conf']) && is_numeric($_GET['conf']) ) && isset($_GET['token']) ) {
$getConference = ClassRegistry::init('Conference')->find('first', ["conditions" => ["conference_id"=>$_GET['conf'] ]]);
$checkToken = ClassRegistry::init('User')->find('first', ["conditions" => ["remember_token"=>$_GET['token'] ]]);
if ($getConference && $checkToken) {
$checkToken['User']['remember_token'] = $this->generateToken();
if ( ClassRegistry::init('User')->save( $checkToken ) ) {
$session->write('auth', true);
$session->write('conferenceId', $_GET['conf']);
$this->redirect('/');
}
}
else {
$session->write('auth', false);
$session->write('conferenceId', null);
}
}
if (! $session->read('auth') || $session->read('conferenceId') == null ) {
echo "No permission!";
exit;
}
}
At the top of the controller:
App::uses('CakeSession', 'Model/Datasource');
When the URL parameters are present, it traps them, does the work, and redirects to the home route without the parameters.
$this->generateToken();
Creates a new token, and overwrites the old one in the database.
There are 2 main controllers. The controller with this code is the main projects controller. The only time it is really hit is the first time you go to the project, and we hit the index method. From there everything else is AJAX calls to the other controller. There is one link, a "home" type link that will hit that index method.
Sometimes these Ajax calls stop working, and clicking that home link will output "No Permission" instead of the expected html in the container the Ajax call outputs too.
Steps to troubleshoot led me to putting this beforeFilter method on the top of the second controller. Now, randomly I'll get no permission. Sometimes, when I'm on the main project that links to this CakePhp project, I click that link, I get no permission right off the bat.
I found this page: cakephp takes me to login page on multiple request and have tried to set the session details like this:
Configure::write('Session', array(
'defaults' => 'php',
'timeout' => '300' // <- added this element
));
And I have tried:
Configure::write('Session.timeout', '300');
Additionally, I have tried cookieTimeout in both of those cases.
I've also tried
Configure::write('Security.level', 'low');
and included
Configure::write('Session.autoRegenerate', true);
In any order, any of these cause the session to bomb out immediately. I get "No permission on page load, and never get anywhere.
The code for this project is honestly crap. The developer who wrote it had mistakes and errors all over the place. On top of that, we are a Laravel shop. We are just trying to keep the project limping along until sometime in the future when we can nuke it from orbit. So we just need to get this working. Any thoughts on what could be causing this? Any other details I am forgetting to include that would help troubleshoot this issue?
Thanks
Reading & writing session data
You can read values from the session using Set::classicExtract() compatible syntax:
CakeSession::read('Config.language');
$key should be the dot separated path you wish to write $value to:
CakeSession::write('Config.language', 'eng');
When you need to delete data from the session, you can use delete:
CakeSession::delete('Config.language');
You should also see the documentation on Sessions and SessionHelper for how to access Session data in the controller and view.
I'm building a feature into a Laravel 5 app that will allow you to set the content of a status banner that will display across the top of the page. We will be using this banner both to display page-specific things (status messages, etc) and site-wide announcements (every user sees the same thing, banner stays the same for awhile).
Right now, I've implemented this by using Laravel sessions to allow banners to be added by calling a helper method from any controller or middleware:
// Call set_banner from in a controller or middleware (for persistent banners)
function set_banner($banner_text, $banner_class, $banner_persistant=false, $replace=false)
{
$banners = session()->get('banners', []);
// Create new banner
$banner = [
'text' => $banner_text,
'type' => $banner_class,
'persistent' => $banner_persistant
];
// Only put banner in array if it's not already there
if( !in_array($banner, $banners) ) {
// Either override existing banners, or add to queue
if( !$replace ) session()->push('banners', $banner);
else session()->put('banners', [$banner]);
}
}
// Called by default in the master.blade.php template
function get_banners()
{
$banners = session()->pull('banners', Array());
foreach( $banners as $banner ) {
// Print out each banner
print '<div class="col-md-12"><div class="text-center alert alert-block alert-'.$banner['type'].'">';
print $banner['text'];
print '</div></div>';
// Push back into the session if banner is marked as persistent
if ( $banner['persistent'] ) session()->push( 'banners', $banner );
}
}
Banners are created in controllers or middleware like this:
set_banner("<b>Note:</b> This is a sample persistant-scope banner set in a controller", "success", true);
Is there a better way to accomplish storing both page-level and site-wide banners? My concerns is that hitting the session on every pageload may be inefficient, especially for banners that won't be changing for long periods of time. Will this approach mess with Laravel's cache, etc?
As you said the banners do not change that often. Hence for me i would implement it using Cache. This improves performance since we need only one use to have the banners cached. And for the rest its retrieved faster from the Cache rather Session.
Do you want to have to change code to change the banner of a given page?
I would suggest instead creating a "pages" package, where each page route name is entered into a database.
From there, from your page service provider you get Page::getModel()->banner_text or something similar.
The method would look for a db result matching the current route name with a result within db.
when a controller method is triggered you simply call
Page::getBannerText()
That method will pull the current route name, pull the page result related to that page if it exists or create it if it does not exist (easy way to get everything). You cache the db query result for X hours, days or whatever so whenever someone else makes a call, you don't even need to deal with any storage on client side.
This allows you to modify the value from a db fascet. Its the more "proper" way to do it.
I have an odd situation. consider the following scenario:
form on page posts to another domain
that domain processes data, and posts back to controller A
controller A processes returned data, and sets flash session vars
controller A redirects to controller B
session values set in controller A are lost.
Now, I have noticed that if I eliminate step 4; the session values are not lost. It is only happening after calling Response::redirect().
Following is the relevant code in controller A's post handler (in this case, $vars is set to Input::post(); and the renew() method does a soap call to another API, and returns response data.):
$success_object = $this->renew( $vars );
$persist_through_redirect = array(
'accountId' => $success_object->account->accountId,
'expireDate' => $success_object->account->expireDate,
'createDate' => $success_object->account->createDate,
'due_amount' => $success_object->account->lastPaymentAmt,
'offer_name' => $offer_name,
'member_name' => $vars['first_name'] . ' ' . $vars['last_name']
);
return $this->redirect('success/', $persist_through_redirect);
And this is the code behind method redirect():
private function redirect($redirect_endpoint, $redirect_values = null) {
Session::set_flash('flash_redirect', $redirect_values);
Response::redirect($redirect_endpoint);
}
Finally, here is the code in controller B that accesses the session data:
$information = Session::get_flash('flash_redirect');
if(isset($information)) {
View::set_global('membership', $information);
}
// set the locale based on fuel's setting
View::set_global('locale', str_replace( '_', '-', Fuel::$locale ));
return View::forge('successes/success_card');
It gets weirder though. Not all session data is lost. Any session data I had set in the before() method is staying put.
I'm very stumped on this one. Any ideas why my session data is lost?
As it turned out, the core issue was that my session cookie was exceeding 4kb.
What was happening is that when I made a call to Response::redirect(), the script was ending at that point, but also causing a redirect that was hiding the exception being thrown. I added a call to Session::write() just before the redirect, which allowed the exception to not be "hidden" due to page reload.
Once I saw this, it was just a matter of tracking down code that was pushing all of this extra data I did not need, and removing it.
I have seen in some MVC applications the use of Token keys to prevent CSRF. A typical example of where it may be used is on the delete method for a post.
And I've seen implementations using both GET and POST methods.
An example GET request link with a token:
https://domain.com/posts/G7j/delete/EOwFwC4TIIydMVUHMXZZdkbUR0cluRSkFzecQy3m5pMTYVXRkcFIBWUZYLNUNSNgQKdnpTWu
And an example of a POST request with a token:
<form action="/posts/G7j/delete" method="post">
<input type="hidden" name="token" value="EOwFwC4TIIydMVUHMXZZdkbUR0cluRSkFzecQy3m5pMTYVXRkcFIBWUZYLNUNSNgQKdnpTWu" />
<button type="submit">Delete</button>
</form>
I've been looking into implementing this into my CakePHP applications based on the documents: http://book.cakephp.org/2.0/en/core-libraries/components/security-component.html
And according to the documents, adding the Security component auto adds the form key to all forms that use the Form Helper.
e.g.
public $components = array(
'Security' => array(
'csrfExpires' => '+1 hour'
)
);
However I have some questions:
1.) Why use POST over GET for some actions such as delete? As the request in the controller will check if the user is authenticated, has permission, and that it has the correct form key.
2.) How can I use the security component with a GET request in CakePHP? Presume I would also need to handle the routing as well.
Firstly CakePHP uses post links to delete as an added level of security because lets say for example your authentication system is not 100% secure and users can access a delete method by manually typing in a URL - if I go in and type /users/delete/10 , I can actually delete users on your server which is risky as you can imagine.
Secondarily GET requests can be cached or bookmarked so users who bookmark these links could end up navigating to broken links which is never a good thing ,also sensitive data will be visible in the URL so for example if someone bookmarks a login page with the GET variables intact - this could compromise there security.
Finally you can easily generate your own tokens using the following code :
$string = Security::hash('string', 'sha1 or md5', true);
print $this->Html->link("Link to somewhere",array("controller"=>"users","action"=>"delete",$string));
The above will use the salt key setup in your core config file but you can also replace true with a custom salt.
For the first question:
The reason probably lies in the definition of HTTP methods. GET is defined as one of the safe methods, which means that it can not be used for changing the state of the server but only for retrieving information. You can read more on the HTTP methods on this link. Since HTML forms are not capable of sending HTTP DELETE request the 'workaround' is to use some of the available methods and if you rule out the GET as a 'safe method' it leaves POST. You can of course use GET to delete stuff, many do, but GET request is by convention expected not to change anything.
edit: If you are interested to read more about HTTP methods and browser/HTML support check out this SO question
johhniedoe pointed me to Croogo 1.3 for an example of how they are have done something similar to what I asked in my question. Because 1.3 was targeted to CakePHP prior to 2.x, the code was a little out of date, so I've amended it as follows:
First create the link (in this case a delete link) with the CSRF Token used by the Security Component passed as a parameter called token.
<?php echo $this->Html->link('Delete', array('action'=>'delete','token'=>$this->params['_Token']['key'])); ?>
Next I've created a wildcard Route Connection to handle the token (this isn't essential usually, but because we're not using the NAMED Token and I wanted to keep the URL cleaner):
Router::connect('/:controller/:action/:token',
array('controller' => 'action'),
array(
'pass' => array('token')
)
);
And then finally handle it in your method like so:
public function delete(){
if (!isset($this->params['token']) || ($this->params['token'] != $this->params['_Token']['key'])) {
$this->Security->blackHoleCallback = '__blackhole';
} else {
// do delete
}
}
This basically says if the token doesn't match then use the blackhole callback function __blackhole, which I define in my AppController to show an error.
One last thing to be aware of though is that you must allow the token to be used more than once and last for example an hour, this is because otherwise the token will be reset and no longer match after the GET request is sent.
public $components = array(
'Security' => array(
'csrfExpires' => '+1 hour',
'csrfUseOnce' => false
)
);