how to integrate sabredav in laravel controller? - laravel

I'm trying to create a SabreDAV-Server in a Laravel Route. The following Code shows that I tried:
Illuminate\Routing\Router::$verbs = [
'GET',
'HEAD',
'POST',
'PUT',
'PATCH',
'DELETE',
'PROPFIND',
'PROPPATCH',
'MKCOL',
'COPY',
'MOVE',
'LOCK',
'UNLOCK'
];
Route::match(['GET', 'HEAD', 'POST', 'PUT', 'DELETE', 'PATCH', 'PROPFIND', 'PROPPATCH', 'MKCOL', 'COPY', 'MOVE', 'LOCK', 'UNLOCK'], 'carddav{test}', function()
{
date_default_timezone_set('Europe/Berlin');
$baseUri = '/carddav';
$pdo = new PDO('mysql:host=localhost;dbname=dav', 'root', 'root');
$pdo->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION);
$authBackend = new \Sabre\DAV\Auth\Backend\PDO($pdo);
$principalBackend = new \Sabre\DAVACL\PrincipalBackend\PDO($pdo);
$carddavBackend = new \Sabre\CardDAV\Backend\PDO($pdo);
$nodes = [
new \Sabre\DAVACL\PrincipalCollection($principalBackend),
new \Sabre\CardDAV\AddressBookRoot($principalBackend, $carddavBackend)
];
$server = new \Sabre\DAV\Server($nodes);
$server->setBaseUri($baseUri);
$server->addPlugin(new \Sabre\DAV\Auth\Plugin($authBackend, 'SabreDAV'));
$server->addPlugin(new \Sabre\DAV\Browser\Plugin());
$server->addPlugin(new \Sabre\CardDAV\Plugin());
$server->addPlugin(new \Sabre\DAVACL\Plugin());
$server->addPlugin(new \Sabre\DAV\Sync\Plugin());
$server->exec();
})->where('path', '(.)*';
But if I try to call it in the Browser there is an error:
<?xml version="1.0" encoding="utf-8"?>
<d:error xmlns:d="DAV:" xmlns:s="http://sabredav.org/ns">
<s:sabredav-version>2.0.4</s:sabredav-version>
<s:exception>Sabre\DAV\Exception\NotAuthenticated</s:exception>
<s:message>No digest authentication headers were found</s:message>
</d:error>
There was no authentication prompt.
If I try to connect from Evolution there was the message: "Method Not Allowed".
Has someone any idea what the problem is?
Thanks,
pepe

The problem is the sent HTTP status code. No matter the response from SabreDAV, the Laravel router always sets the HTTP status code to 200, so no CardDAV client will ever know they have to authorize requests – ignoring the Basic Auth Challenge.
My solution might not be the most elegant one, but it is working. Just wrap the $server->exec() in ob_start() and ob_end() tags and output the content with a real Laravel response:
ob_start();
$server->exec();
$status = $server->httpResponse->getStatus();
$content = ob_get_contents();
ob_end_clean();
return response($content, $status);
General guidance:
Use "postman" (Google Chrome App) to test requests, you'll see they are working when sending authorization headers upfront
Use a web debugging proxy like "Charles" to monitor actual request and response bodies

Related

Enable CORS on Lumen API

I know it's a known issue but I've tried almost everything and I'm still stuck on this. I have a simple project structured like this:
[Client] => [Gateway] => [API]
Laravel 6 Lumen 6 Lumen 6
localhost:8000 localhost:8001 localhost:8002
Since I'm just started working on this project only to prove if this works I've disabled all auth stuff.
On my API I have a folder within public called uploads (Basically in http://localhost:8002/uploads/audio.amr) where I have 1 audio file (.amr) and I'm trying to play it from a client view.
Since html can't play .amr files, I had to use a plugin. And I'm using this one BenzAMRRecorder.
[Client side]
I make an ajax petition to get the url of the audio file. The client through guzzle connects with the gateway and the gateway also does it with the API and I successfully got the url http://localhost:8002/uploads/audio.amr.
$.ajaxSetup({
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
}
});
$.ajax({
url : 'client/get_url_audio',
type : 'GET',
data : {
},
dataType:'json',
success : function(data) {
/** Here's the way to play the file */
var amr = new BenzAMRRecorder();
amr.initWithUrl(data['url']).then(function() {
amr.play();
});
},
});
I successfully got the url but when the BenzAMRRecorder try to access to the url http://localhost:8002/uploads/audio.amr I got this error:
The error:
Access to XMLHttpRequest at 'http://localhost:8002/uploads/audio.amr' from origin 'http://localhost:8000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
I've read a lot of ways to fix this and I added a CorsMiddleware on the API with a handle function as follows:
public function handle($request, Closure $next)
{
$headers = [
'Access-Control-Allow-Origin' => '*',
'Access-Control-Allow-Methods' => 'POST, GET, OPTIONS, PUT, DELETE',
'Access-Control-Allow-Credentials' => 'true',
'Access-Control-Max-Age' => '86400',
'Access-Control-Allow-Headers' => 'Content-Type, Authorization, X-Requested-With'
];
if ($request->isMethod('OPTIONS'))
{
return response()->json('{"method":"OPTIONS"}', 200, $headers);
}
$response = $next($request);
foreach($headers as $key => $value)
{
$response->header($key, $value);
}
return $response;
}
And then on bootstrap/app.php added
$app->middleware([
App\Http\Middleware\Cors::class
]);
But I'm still getting the same error. The thing I thought is that, when the method amr.initWithUrl(data['url']) access to the API folder, it doesn't go to middleware and try to access directly to the folders without passing by the middleware but I don't know why. Can someone help me to solve this problem?
EDIT: I also tried with github.com/barryvdh/laravel-cors
Add the following in the .htaccess file from the server which holds the resource you are trying to access:
Header Set Access-Control-Allow-Origin "*"
I don't know if it works in Lumen, but for Laravel, I've had a lot of success using this neomerx/cors package.
You probably missed the header X-CSRF-TOKEN from your CORS middleware?
$headers = [
....
// You will need to add ALL headers sent from your client
'Access-Control-Allow-Headers' => 'Content-Type, Authorization, X-Requested-With, X-CSRF-TOKEN'
];

How to resolve Laravel 401 (Unauthorized) error

How to resolve Laravel 401 (Unauthorized) error for a particular single URL.
url is accessible directly but when request send using axios its how this error.
api_token: this.user.api_token
axios.post("http://foo",
{headers: { 'Authorization' : 'Bearer '+ api_token}})
.then(response => {
//action
})
link: https://forum.vuejs.org/t/401-unauthorized-vuejs-laravel-and-passport/59770
or
postComment() {
axios.post('/api/posts/'+this.post.id+'/comment', {
api_token: this.user.api_token,
body: this.commentBox
})
but make sure that you have "user.api_token"
Some people just assume all has been configured right out of the box; but you need to:
Follow this Laravel documentation to gain api_token for all your users.
Laravel api authentication
NOTE: When you register users, if users api_token in database is still being saved as NULL, go to the users model and add api_token to fillable array
//Model/User.php
protected $fillable = [
...
'api_token',
...
];
In your view layout app.blade.php create a meta for your token, just like your csrf:
//views/layout/app.blade.php
<!-- APIToken -->
<meta name="api_token" content="{{ Auth::user()->api_token }}">
Finally in your main.js or app.js; you can include it with every sent request
//resources/app.js
window.axios.defaults.headers.common = {
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content'),
'Authorization': 'Bearer '+ document.querySelector('meta[name="api_token"]').getAttribute('content'),
'X-Requested-With': 'XMLHttpRequest'
};
This works and would help someone after all I've been through; meanwhile Laravel Passport and Sanctum are better recommendation for api authentication

CORS Access to XMLHttpRequest at X from origin has been blocked by CORS policy

CORS Access to XMLHttpRequest at X from origin has been blocked by CORS policy: Response to preflight request doesn't pass access control check: It does not have HTTP ok status.
Hi, I am struggling to resolve my problems with CORS denying interaction of my Vue component with an external API with axios since it returns this error. I've tried both using Barryvdh's Cors header support and making a middleware and custom route. It simply won't work. Everything that has been mentioned in README.md in Barryvdh's repo has been done and unfortunately, this problem won't get resolved by any means necessary.
Here is the code, even though I don't think there's need to show since it's exactly the same as mentioned in the repo;
inside Kernel.php:
protected $middleware = [
\Barryvdh\Cors\HandleCors::class,
inside app.php (providers array):
Barryvdh\Cors\ServiceProvider::class,
config/cors.php:
'supportsCredentials' => false,
'allowedOrigins' => ['*'],
'allowedHeaders' => ['*'],
'allowedMethods' => ['*'], // ex: ['GET', 'POST', 'PUT', 'DELETE']
'exposedHeaders' => [],
'maxAge' => 0,
Here's the axios get call (I've replaced my token with 'TOKEN')
methods: {
loadWeatherData: function() {
axios.get( 'http://api.openweathermap.org/data/2.5/weather?q=London&mode=json&units=metric&appid=TOKEN' )
.then( function( response ) {
console.log( 'radi' );
}).catch( errors => {
console.log( errors+' ne radi');
});
}
},
I've composer dump-ed, nothing affected resolving the problem.
Is there something I am doing wrong and are there any solutions for this problem? Thanks in advance!
The problem here seems to be that axios likes to send its own default headers, and these don't pass the preflight check for your external request. To fix this, you will need to remove the offending headers.
I was able to recreate your error, and also to bypass the CORS issue using the code below.
let url = 'https://api.openweathermap.org/data/2.5/weather?q=London&mode=json&units=metric&appid=TOKEN';
// create a new instance so we don't delete the headers for other requests
let instance = axios.create();
// delete headers if existing
delete instance.defaults.headers.common['Accept'];
delete instance.defaults.headers.common['X-Requested-With'];
delete instance.defaults.headers.common['X-CSRF-TOKEN'];
// use the new axios instance to make your get request
instance.get(url)
.then(function(response) {
console.log(response);
}).catch( errors => {
console.log(errors + ' ne radi');
});
Hope this helps and good luck!
You can add into TrustHosts.php Middleware without doing anything extra. Read more from here https://stackoverflow.com/a/70361284/2612926

Laravel issue No 'Access-Control-Allow-Origin'

I'm writing a very common topic, but I am desperate, wasted two days trying to make this work.
I'm on Chrome, trying to make my first Login/Register app and at the time of sending the data to the backend, i get the following error:
XMLHttpRequest cannot load http://192.168.1.133:8100/api/login. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:8100' is therefore not allowed access. The response had HTTP status code 500.
I am making a POST request from AngularJS like follows:
$scope.data = "UserName"; // for example
$scope.register = function() {
var withAjax = $http.post('http://192.168.1.133/api/register', $scope.data);
withAjax
.success(function(answer){
console.log(answer.message);
})
.error(function(answer) {
console.log("error");
});
}
])
And the Laravel API backend is simple, just return 'Correct'.
public function login (Request $request) {
$inputArray = $request->all();
return response()->json(["message" => "Login: All Correct"]);
}
In my front-end app I alowed all kind connections with:
<allow-navigation href="http://*/*" />
<allow-navigation href="*" />
<access origin="*"/>
In PHP I tried all. Disabled CSFR token for API, and installed properly "barryvdh/laravel-cors" and configured cors file like follows:
return [
'supportsCredentials' => false,
'allowedOrigins' => ['*'],
'allowedHeaders' => ['*'],
'allowedMethods' => ['*'], // ex: ['GET', 'POST', 'PUT', 'DELETE']
'exposedHeaders' => [],
];
When I can make, sending data back and forth easy process, I will care about protection. Now I decided to open 'all' to make it straightforward.
I'm crazy about this, lost two days just trying to solve this.
Any clue would be of help.
Thank you!
Try this plugin for Chrome, might help you. CORS

Yii2 CORS inserts two records

I am building an API with Yii2 and have enabled the CORS filter to handle requests from a web frontend which is working.
However because of the pre-flight OPTIONS request and then the real POST request I am getting two records added to the database, one for each request. I would have thought that Yii should accept the OPTIONS request, return the correct headers and then exit. Why does it actually process the full request?
I am working around this for now by adding this to the top of the controller action:
if(Yii::$app->request->getMethod() == 'OPTIONS') {
return;
}
Is that the best approach or am I missing something?
That should be wrong because a browser need the options response to know the allowed list of verbs he can send. Otherwise a 401 error may be raised. Its source code can be seen here:
class OptionsAction extends \yii\base\Action
{
public $collectionOptions = ['GET', 'POST', 'HEAD', 'OPTIONS'];
public $resourceOptions = ['GET', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS'];
public function run($id = null)
{
if (Yii::$app->getRequest()->getMethod() !== 'OPTIONS') {
Yii::$app->getResponse()->setStatusCode(405);
}
$options = $id === null ? $this->collectionOptions : $this->resourceOptions;
Yii::$app->getResponse()->getHeaders()->set('Allow', implode(', ', $options));
}
}
And that is all what it does: sending a list of allowed verbs within a response headers.
Maybe the POST request has been sent twice from client script due to unexpected responses. Try to apply the answer I posted in your other question instead. I think it will also solve this:
Yii2 CORS with Auth not working for non CRUD actions.

Resources