How to set SameSite=none in drupal setcookie function - drupal

If there any hook or alter that which is used to add "SameSite=none" parameter in set cookies in drupal 7.

This one is kind of tricky, since PHP 5 does not have a flag for SameSite:None; Secure; when using setcookie() or setrawcookie(). PHP 7 does, however since these functions don't escape semicolons, we can add it to the 'domain' flag and it will get set...
Important: this hack WILL BREAK in PHP 7.3. If you're going to use it, at least wrap it in a PHP version check like if (PHP_VERSION_ID < 70300) { ... } else { ... }
setrawcookie('cookie_name', 'cookie_value', time() + 7700, '/', 'example.com; SameSite=none;');
For earlier versions of PHP, you can set the header() directly:
header('Set-Cookie: cookie_name=cookie_value; SameSite=None;');
As of PHP 7.3.0 the setcookie() method supports the SameSite attribute in its options and will accept None as a valid value. See https://www.php.net/manual/en/function.setcookie.php
setcookie($name, $value, [
'expires' => time() + 86400,
'path' => '/',
'secure' => true,
'samesite' => 'None',
]);

Related

Symfony 3.2, authentification and cross-site cookies

I know that Symfony 3.2 is not maintained for very long time, but it happens that my company use it.
And now we have a problem with authentifacation. We have widget that can be (and is) installed on multiple sites, so user can log in there. But database and backend code in Symfony 3.2 is stored in our site. And for authentification we use standard Symfony cookies, which are in our case cross-site.
But now Google Chrome is restricted access to cross-domain cookies which are not marked as SameSite=None. So authentification in Chrome doesn't work anymore.
I know that in Symfony 4.2 I can solve this problem by setting security parameter in security.yaml. But in Symfony 3.2 it doesn't work.
Is there any way to solve this problem except for upgrade to Symfony 4?
From https://github.com/GoogleChromeLabs/samesite-examples/blob/master/php.md
As of PHP 7.3.0 the
setcookie() method
supports the SameSite attribute in its options and will accept None as a
valid value.
// Set a same-site cookie for first-party contexts
setcookie('cookie1', 'value1', ['samesite' => 'Lax']);
// Set a cross-site cookie for third-party contexts
setcookie('cookie2', 'value2', ['samesite' => 'None', 'secure' => true]);
For earlier versions of PHP, you can also set the
header() directly:
// Set a same-site cookie for first-party contexts
header('Set-Cookie: cookie1=value1; SameSite=Lax', false);
// Set a cross-site cookie for third-party contexts
header('Set-Cookie: cookie2=value2; SameSite=None; Secure', false);
For Session Cookie , you can set into session_set_cookie_params method.
PHP 7.3.0 introduced new attributes for samesite.
if (PHP_VERSION_ID >= 70300) {
session_set_cookie_params([
'lifetime' => $cookie_timeout,
'path' => '/',
'domain' => $cookie_domain,
'secure' => $session_secure,
'httponly' => $cookie_httponly,
'samesite' => 'Lax'
]);
} else {
session_set_cookie_params(
$cookie_timeout,
'/; samesite=Lax',
$cookie_domain,
$session_secure,
$cookie_httponly
);
}
There are third-party packages, such as delight-im/PHP-Cookie, which supports the SameSite attribute and the None value (since v3.2) on PHP 5.4.0+.

Drupal 8, http request to server and append to the site

I have a Drupal 8 site and I need to make a http request to another server (for content) and append it into the page like footer. I can't do this after DOM is loaded because of SEO issues.
I'm familiar with WordPress and so easy to do it with WP. However, I'm confused about how to do this with .twig, Drupal 8. Any suggestions would be great. Thanks.
If you want the content to be part of the DOM when it is sent to the browser this is not something you want to do in Twig, you should have the content loaded earlier in the process.
You can create a module that defines custom block and place that block in the correct region of your theme.
The block plugin class requires you to write a build() method that returns a render array for your block. Within build() you can do whatever you need to acquire the content, including making an HTTP Request using Symfony's Guzzle client:
public function build() {
$url = 'https://www.example.com/remote/service';
$client = \Drupal::httpClient();
$request = $client->createRequest('GET', $url);
// Do whatever's needed to extract the data you need from the request...
$build = ['my_remote_block' => [
'#theme' => 'my_custom_theme_function',
'#attributes' => [
//An array of variables to pass to the theme
],
'#cache' => [
//Some appropriate cache settings
],
],
];
If you are getting HTML back from your request you could skip the custom theme function and return an array with '#type' => 'markup' and then a field for the markup. The rest of this example assumes you get data back and want to render it yourself.
In your module's .module file you can define the custom theme function (so you can use a twig file of your own design).
function my_module_theme($existing, $type, $theme, $path) {
return [
'my_custom_theme_function' => [
'variables'=> [
// defaults for variables used in this block.
],
],
];
}
Then finally you can create a twig file named my-custom-theme-function.html.twig to render the output.
Often these kinds of setups are quite slow (since the browser's request then triggers another HTTP request + processing time) so you should consider either caching the block as much as possible or using a technique like BigPipe (which is probably not an option for you based on your question but seemed worth pointing out).

HTTP client Cakephp 3 ignores json body

I'm currently writing a RESTful API in Cakephp 3 whereby I need to test a POST operation through http://host.com/api/pictures. The code for the test:
<?php
namespace App\Test\TestCase\Controller;
use App\Controller\Api\UsersController;
use Cake\TestSuite\IntegrationTestCase;
use Cake\Network\Http\Client;
use Cake\Network\Http\FormData;
class ApiPicturesControllerTest extends IntegrationTestCase{
public $fixtures = [
'app.users',
'app.comments',
'app.albums',
'app.users_albums'
];
public function testAdd(){
// $data = new FormData();
$accessToken ='eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOjksImV4cCI6MTQ1NzYyOTU3NH0.NnjXWEQCno3PUiwHhnUCBjiknR-NlmT42oPLA5KhuYo';
$http = new Client([
'headers' => ['Authorization' => 'Bearer ' . $accessToken, 'Content-Type' => 'application/json']
]);
$data = [
"album_id" => 1,
"link" => "http://www.google.com",
"description" => "testtesttest",
"favorite" => true
];
$result = $http->post('http://vecto.app/api/pictures/add.json', $data, ['type'=>'json']);
// $this->assertResponseOk();
// debug($result);
}
}
When I try to debug the result I get a 'cannot add or update child row' while I'm sure the responding id does exists
(the fixtures does have the id's too). Additionally, the log indicates that it only tries to insert the create/update rows. Therefore, I'm pretty sure the data is ignored but however I can't find a solution. I already tried different combination of headers like only application/json for Accept, application/json for Content-Type etc. I'm using the CRUD plugin for Cakephp to pass the data to an add function.
Postman output
Furthermore, I tried the Postman Chrome plugin to save the data and that actually does work. Does anyone know what I'm doing wrong in the test?
That's not how the integration test case is ment to be used. You are dispatching an external, real request, which will leave the test environment, while you should use the request dispatching tools that the integration test case supplies, that is
IntegrationTestCase::get()
IntegrationTestCase::post()
IntegrationTestCase::put()
etc...
These methods will dispatch simulated requests that do not leave the test environment, which is crucial for things to work properly, as you want to use test connections, inspect possible exceptions, have access to the used session, etc...
ie, you should do something along the lines of
$accessToken = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOjksImV4cCI6MTQ1NzYyOTU3NH0.NnjXWEQCno3PUiwHhnUCBjiknR-NlmT42oPLA5KhuYo';
$this->configRequest([
'headers' => [
'Authorization' => 'Bearer ' . $accessToken,
'Content-Type' => 'application/json'
]
]);
$data = [
"album_id" => 1,
"link" => "http://www.google.com",
"description" => "testtesttest",
"favorite" => true
];
$this->post('/api/pictures/add.json', json_encode($data));
Note that a content type of application/json will require you to send raw JSON data! If you don't actually need/want to test parsing of raw input, then you could skip that header, and pass the array as data instead.
See also
Cookbook > Testing > Controller Integration Testing
API > \Cake\TestSuite\IntegrationTestCase

getUrl dropping _format when set to json

I'm trying to generate a url and set the _format variable to json. the .json part is never added to the route. setting to html or xml appends the format correctly.
The route from app/console router:debug,
api_1_get_page GET ANY ANY /api/page/{id}.{_format}
And my functional test code,
$this->getUrl('api_1_get_page', array('id' => $page->getId(), '_format' => 'json'));
when I dump this I get,
string(18) "/api/pages/1"
Whereas,
$this->getUrl('api_1_get_page', array('id' => $page->getId(), '_format' => 'html'));
returns string(18) "/api/pages/1.html"
I'm guessing there is a setting controlling this, I've tried setting every fos_rest config setting on and off and nothing is making a difference. This is also causing me issues when tests are checking the Location: header expecting .json.
Have you set .json as the default format in your route?
If you're using FOSRestBundle look at routing_loader in the FOSRestBundle config
See: https://github.com/FriendsOfSymfony/FOSRestBundle/blob/master/Resources/doc/5-automatic-route-generation_single-restful-controller.md#routing

Edit include/mail.inc, add my own SMTP setting there

The following function is contained in include/mail.inc of Drupal6, it uses the default SMTP settings buried in a file named "php.ini" to send mail.
function drupal_mail_send($message) {
// Allow for a custom mail backend.
if (variable_get('smtp_library', '') && file_exists(variable_get('smtp_library', ''))) {
include_once './'. variable_get('smtp_library', '');
return drupal_mail_wrapper($message);
}
else {
$mimeheaders = array();
foreach ($message['headers'] as $name => $value) {
$mimeheaders[] = $name .': '. mime_header_encode($value);
}
return mail(
$message['to'],
mime_header_encode($message['subject']),
// Note: e-mail uses CRLF for line-endings, but PHP's API requires LF.
// They will appear correctly in the actual e-mail that is sent.
str_replace("\r", '', $message['body']),
// For headers, PHP's API suggests that we use CRLF normally,
// but some MTAs incorrecly replace LF with CRLF. See #234403.
join("\n", $mimeheaders)
);
}
}
but I use shared host, therefore i can't edit php.ini, i want to edit the above function "drupal_mail_send", add the codes below into that function so that it can bypass the PHP mail() function and send email directly to my favorite SMTP server.
include('Mail.php');
$recipients = array( 'someone#example.com' ); # Can be one or more emails
$headers = array (
'From' => 'someone#example.com',
'To' => join(', ', $recipients),
'Subject' => 'Testing email from project web',
);
$body = "This was sent via php from project web!\n";
$mail_object =& Mail::factory('smtp',
array(
'host' => 'prwebmail',
'auth' => true,
'username' => 'YOUR_PROJECT_NAME',
'password' => 'PASSWORD', # As set on your project's config page
#'debug' => true, # uncomment to enable debugging
));
$mail_object->send($recipients, $headers, $body);
Could you write down the modified code for my reference?
The code in drupal_mail_send is part o the Drupal core functionality and should not be changed directly as your changes may be overwritten when you update Drupal.
Modifications of Drupal core files is often referred to by the Drupal community as "hacking core" and is largely discouraged.
Drupal already has a number of modules available which may help you. See:
http://drupal.org/project/phpmailer module:
Adds SMTP support for sending e-mails using the PHPMailer library.
Comes with detailed configuration instructions for how to use Google
Mail as mail server.
http://drupal.org/project/smtp module:
This module allows Drupal to bypass the PHP mail() function and send
email directly to an SMTP server.

Resources