diff --git a/class.paperless.php b/class.paperless.php index 50b30f1..c95966f 100644 --- a/class.paperless.php +++ b/class.paperless.php @@ -110,6 +110,7 @@ class SeedDMS_ExtPaperless_Bootstrap { /* {{{ */ } /* }}} */ use Psr\Container\ContainerInterface; +use Psr\Http\Message\ResponseInterface; class SeedDMS_ExtPaperless_Process_Folder { /* {{{ */ protected $list; @@ -128,8 +129,27 @@ class SeedDMS_ExtPaperless_Process_Folder { /* {{{ */ } /* }}} */ } /* }}} */ +final class SeedDMS_ExtPaperless_JsonRenderer { /* {{{ */ + public function json( + ResponseInterface $response, + array $data = null + ): ResponseInterface { + $response = $response->withHeader('Content-Type', 'application/json'); + + $response->getBody()->write( + (string)json_encode( + $data, + JSON_UNESCAPED_SLASHES | JSON_PARTIAL_OUTPUT_ON_ERROR + ) + ); + + return $response; + } +} /* }}} */ + class SeedDMS_ExtPaperless_RestAPI_Controller { /* {{{ */ protected $container; + protected $renderer; static public function mb_word_count($string, $mode = MB_CASE_TITLE, $characters = null) { /* {{{ */ $string = mb_convert_case($string, $mode, "UTF-8"); @@ -255,8 +275,9 @@ class SeedDMS_ExtPaperless_RestAPI_Controller { /* {{{ */ } /* }}} */ // constructor receives container instance - public function __construct(ContainerInterface $container) { /* {{{ */ + public function __construct(ContainerInterface $container, SeedDMS_ExtPaperless_JsonRenderer $renderer) { /* {{{ */ $this->container = $container; + $this->renderer = $renderer; } /* }}} */ function api($request, $response) { /* {{{ */ @@ -273,7 +294,7 @@ class SeedDMS_ExtPaperless_RestAPI_Controller { /* {{{ */ 'mail_rule'=>$request->getUri().'mail_rule/', ); - return $response->withJson($data, 200); + return $this->renderer->json($response, $data)->withStatus(200); } /* }}} */ function token($request, $response) { /* {{{ */ @@ -289,7 +310,7 @@ class SeedDMS_ExtPaperless_RestAPI_Controller { /* {{{ */ if($data) { $userobj = $authenticator->authenticate($data['username'], $data['password']); if(!$userobj) - return $response->withJson(array('non_field_errors'=>['Unable to log in with provided credentials.']), 403); + return $this->renderer->json($response, array('non_field_errors'=>['Unable to log in with provided credentials.']))->withStatus(403); else { if(!empty($settings->_extensions['paperless']['jwtsecret'])) { $token = new SeedDMS_JwtToken($settings->_extensions['paperless']['jwtsecret']); @@ -300,9 +321,9 @@ class SeedDMS_ExtPaperless_RestAPI_Controller { /* {{{ */ if(!$tokenstr = $token->jwtEncode($userobj->getId().':'.(time()+$days*84600))) { return $response->withStatus(403); } - return $response->withJson(array('token'=>$tokenstr), 200); + return $this->renderer->json($response, array('token'=>$tokenstr))->withStatus(200); } else { - return $response->withJson(array('token'=>$settings->_apiKey), 200); + return $this->renderer->json($response, array('token'=>$settings->_apiKey))->withStatus(200); } } } @@ -317,7 +338,7 @@ class SeedDMS_ExtPaperless_RestAPI_Controller { /* {{{ */ $logger = $this->container->get('logger'); if(false === ($categories = $dms->getDocumentCategories())) { - return $response->withJson(array('results'=>null), 500); + return $this->renderer->json($response, array('results'=>null))->withStatus(500); } if(!empty($settings->_extensions['paperless']['usehomefolder'])) { @@ -347,7 +368,7 @@ class SeedDMS_ExtPaperless_RestAPI_Controller { /* {{{ */ $tmp['document_count'] = (int) $facets['category'][$category->getName()]; $data[] = $tmp; } - return $response->withJson(array('count'=>count($data), 'next'=>null, 'previous'=>null, 'results'=>$data), 200); + return $this->renderer->json($response, array('count'=>count($data), 'next'=>null, 'previous'=>null, 'results'=>$data))->withStatus(200); } /* }}} */ function post_tag($request, $response) { /* {{{ */ @@ -364,13 +385,13 @@ class SeedDMS_ExtPaperless_RestAPI_Controller { /* {{{ */ $data = $request->getParsedBody(); $oldcat = $dms->getDocumentCategoryByName($data['name']); if (is_object($oldcat)) { - return $response->withJson(getMLText('paperless_tag_already_exists'), 400); + return $this->renderer->json($response, getMLText('paperless_tag_already_exists'))->withStatus(400); } $newCategory = $dms->addDocumentCategory($data['name']); if (!$newCategory) - return $response->withJson(getMLText('paperless_could_not_create_tag'), 400); + return $this->renderer->json($response, getMLText('paperless_could_not_create_tag'))->withStatus(400); - return $response->withJson($this->__getCategoryData($newCategory, []), 201); + return $this->renderer->json($response, $this->__getCategoryData($newCategory, []), 201); } /* }}} */ function delete_tag($request, $response, $args) { /* {{{ */ @@ -433,7 +454,7 @@ class SeedDMS_ExtPaperless_RestAPI_Controller { /* {{{ */ ); } } - return $response->withJson(array('count'=>count($correspondents), 'next'=>null, 'previous'=>null, 'results'=>$correspondents), 200); + return $this->renderer->json($response, array('count'=>count($correspondents), 'next'=>null, 'previous'=>null, 'results'=>$correspondents))->withStatus(200); } /* }}} */ function document_types($request, $response) { /* {{{ */ @@ -459,7 +480,7 @@ class SeedDMS_ExtPaperless_RestAPI_Controller { /* {{{ */ ); } } - return $response->withJson(array('count'=>count($types), 'next'=>null, 'previous'=>null, 'results'=>$types), 200); + return $this->renderer->json($response, array('count'=>count($types), 'next'=>null, 'previous'=>null, 'results'=>$types))->withStatus(200); } /* }}} */ function saved_views($request, $response) { /* {{{ */ @@ -477,7 +498,7 @@ class SeedDMS_ExtPaperless_RestAPI_Controller { /* {{{ */ $tmp = $view->getView(); $data[] = $tmp; } - return $response->withJson(array('count'=>count($data), 'next'=>null, 'previous'=>null, 'results'=>$data), 200); + return $this->renderer->json($response, array('count'=>count($data), 'next'=>null, 'previous'=>null, 'results'=>$data))->withStatus(200); } /* }}} */ function post_saved_views($request, $response) { /* {{{ */ @@ -497,9 +518,9 @@ class SeedDMS_ExtPaperless_RestAPI_Controller { /* {{{ */ $view->setDMS($dms); if($newview = $view->save()) { // $logger->log(var_export($newview, true), PEAR_LOG_DEBUG); - return $response->withJson($newview->getView(), 201); + return $this->renderer->json($response, $newview->getView(), 201); } else { - return $response->withJson('', 501); + return $response->withStatus(501); } } /* }}} */ @@ -547,7 +568,7 @@ class SeedDMS_ExtPaperless_RestAPI_Controller { /* {{{ */ $view->setView($data); $view->save(); } - return $response->withJson($view->getView(), 200); + return $this->renderer->json($response, $view->getView(), 200); } /* }}} */ function storage_paths($request, $response) { /* {{{ */ @@ -572,7 +593,7 @@ class SeedDMS_ExtPaperless_RestAPI_Controller { /* {{{ */ if($path[1] > 0) $paths[] = array('id'=>(int)$fid, 'name'=>$path[0], 'slug'=>$path[0], 'path'=>$path[0], 'match'=>'', 'matching_algorithm'=>6, 'is_insensitive'=>true, 'document_count'=>$path[1]); } - return $response->withJson(array('count'=>count($paths), 'next'=>null, 'previous'=>null, 'results'=>$paths), 200); + return $this->renderer->json($response, array('count'=>count($paths), 'next'=>null, 'previous'=>null, 'results'=>$paths))->withStatus(200); } /* }}} */ function documents($request, $response) { /* {{{ */ @@ -773,7 +794,7 @@ class SeedDMS_ExtPaperless_RestAPI_Controller { /* {{{ */ } $logger->log('Result is '.$allids, PEAR_LOG_DEBUG); if($recs) - return $response->withJson(array('count'=>$searchresult['count'], 'next'=>null, 'previous'=>null, 'offset'=>$offset, 'limit'=>$limit, 'results'=>$recs), 200); + return $this->renderer->json($response, array('count'=>$searchresult['count'], 'next'=>null, 'previous'=>null, 'offset'=>$offset, 'limit'=>$limit, 'results'=>$recs))->withStatus(200); else { /* Still nothing found, so try a shorter query */ array_pop($newquery); @@ -791,7 +812,7 @@ class SeedDMS_ExtPaperless_RestAPI_Controller { /* {{{ */ } } - return $response->withJson(array('count'=>0, 'next'=>null, 'previous'=>null, 'offset'=>0, 'limit'=>$limit, 'results'=>[]), 200); + return $this->renderer->json($response, array('count'=>0, 'next'=>null, 'previous'=>null, 'offset'=>0, 'limit'=>$limit, 'results'=>[]))->withStatus(200); /* Get all documents in the same folder and subfolders $likeid = (int) $params['more_like_id']; if($likeid && $likedoc = $dms->getDocument($likeid)) { @@ -900,19 +921,19 @@ class SeedDMS_ExtPaperless_RestAPI_Controller { /* {{{ */ } if($offset + $limit < $searchresult['count']) { $params['page'] = $page+1; - $next = $request->getUri()->getBasePath().'/api/documents?'.http_build_query($params); + $next = $request->getUri()->getPath().'?'.http_build_query($params); } else $next = null; if($offset > 0) { $params['page'] = $page-1; - $prev = $request->getUri()->getBasePath().'/api/documents?'.http_build_query($params); + $prev = $request->getUri()->getPath().'?'.http_build_query($params); } else $prev = null; - return $response->withJson(array('count'=>$searchresult['count'], 'next'=>$next, 'previous'=>$prev, 'offset'=>$offset, 'limit'=>$limit, 'results'=>$recs), 200); + return $this->renderer->json($response, array('count'=>$searchresult['count'], 'next'=>$next, 'previous'=>$prev, 'offset'=>$offset, 'limit'=>$limit, 'results'=>$recs))->withStatus(200); } } } - return $response->withJson('Error', 500); + return $response->withStatus(500); } /* }}} */ @@ -931,12 +952,12 @@ class SeedDMS_ExtPaperless_RestAPI_Controller { /* {{{ */ if($document->getAccessMode($userobj) >= M_READ) { /* get untruncated content from file */ $rec = $this->__getDocumentData($document, true, true); - return $response->withJson($rec, 200); + return $this->renderer->json($response, $rec)->withStatus(200); } else { return $response->withStatus(404); } } - return $response->withJson('Error', 500); + return $response->withStatus(500); } /* }}} */ /** @@ -971,7 +992,7 @@ class SeedDMS_ExtPaperless_RestAPI_Controller { /* {{{ */ } - return $response->withJson($list, 200); + return $this->renderer->json($response, $list)->withStatus(200); } /* }}} */ function ui_settings($request, $response) { /* {{{ */ @@ -1031,7 +1052,7 @@ class SeedDMS_ExtPaperless_RestAPI_Controller { /* {{{ */ 'settings'=>array('update_checking'=>array('backend_setting'=>'default')), ); */ - return $response->withJson($data, 200); + return $this->renderer->json($response, $data)->withStatus(200); } /* }}} */ function statstotal($request, $response) { /* {{{ */ @@ -1042,7 +1063,7 @@ class SeedDMS_ExtPaperless_RestAPI_Controller { /* {{{ */ $logger = $this->container->get('logger'); if(false === ($categories = $dms->getDocumentCategories())) { - return $response->withJson(array('results'=>null), 500); + return $this->renderer->json($response, array('results'=>null))->withStatus(500); } if(!empty($settings->_extensions['paperless']['usehomefolder'])) { @@ -1087,11 +1108,12 @@ class SeedDMS_ExtPaperless_RestAPI_Controller { /* {{{ */ } } - return $response->withJson($data, 200); + return $this->renderer->json($response, $data)->withStatus(200); } /* }}} */ function fetch_thumb($request, $response, $args) { /* {{{ */ - return $response->withRedirect($request->getUri()->getBasePath().'/api/documents/'.$args['id'].'/thumb/', 302); + $p = strpos($request->getUri()->getPath(), '/fetch/thumb'); + return $response->withHeader('Location', substr($request->getUri()->getPath(), 0, $p).'/api/documents/'.$args['id'].'/thumb/')->withStatus(302); } /* }}} */ function documents_thumb($request, $response, $args) { /* {{{ */ @@ -1118,17 +1140,15 @@ class SeedDMS_ExtPaperless_RestAPI_Controller { /* {{{ */ $previewer->createPreview($object); $file = $previewer->getFileName($object, $width).".png"; - if(!($fh = @fopen($file, 'rb'))) { - return $response->withJson(array('success'=>false, 'message'=>'', 'data'=>''), 500); - } - $stream = new \Slim\Http\Stream($fh); // create a stream instance for the response body + if(!file_exists($file)) + return $this->renderer->json($response, array('success'=>false, 'message'=>'', 'data'=>''))->withStatus(500); + $response->getBody()->write(file_get_contents($file)); return $response->withHeader('Content-Type', 'image/png') ->withHeader('Content-Description', 'File Transfer') ->withHeader('Content-Transfer-Encoding', 'binary') ->withHeader('Content-Disposition', 'attachment; filename="preview-' . $document->getID() . "-" . $object->getVersion() . "-" . $width . ".png" . '"') - ->withHeader('Content-Length', $previewer->getFilesize($object)) - ->withBody($stream); + ->withHeader('Content-Length', $previewer->getFilesize($object)); } } return $response->withStatus(403); @@ -1137,7 +1157,8 @@ class SeedDMS_ExtPaperless_RestAPI_Controller { /* {{{ */ function fetch_doc($request, $response, $args) { /* {{{ */ $logger = $this->container->get('logger'); $logger->log('Fetch doc '.$args['id'], PEAR_LOG_INFO); - return $response->withRedirect($request->getUri()->getBasePath().'/api/documents/'.$args['id'].'/download/', 302); + $p = strpos($request->getUri()->getPath(), '/fetch/doc'); + return $response->withHeader('Location', substr($request->getUri()->getPath(), 0, $p).'/api/documents/'.$args['id'].'/download/')->withStatus(302); } /* }}} */ /** @@ -1166,8 +1187,8 @@ class SeedDMS_ExtPaperless_RestAPI_Controller { /* {{{ */ else $filename = $document->getName().$lc->getFileType(); $file = $dms->contentDir . $lc->getPath(); - if(!($fh = @fopen($file, 'rb'))) { - return $response->withJson(array('success'=>false, 'message'=>'', 'data'=>''), 500); + if(!file_exists($file)) { + return $this->renderer->json($response, array('success'=>false, 'message'=>'', 'data'=>''))->withStatus(500); } $filesize = filesize($dms->contentDir . $lc->getPath()); } else { @@ -1180,19 +1201,18 @@ class SeedDMS_ExtPaperless_RestAPI_Controller { /* {{{ */ $previewer->createPreview($lc); if(!$previewer->hasPreview($lc)) { $logger->log('Creating pdf preview failed', PEAR_LOG_ERR); - return $response->withJson('', 500); + return $this->renderer->json($response, array('success'=>false, 'message'=>'Creating pdf preview failed', 'data'=>''))->withStatus(500); } else { $filename = $document->getName().".pdf"; $file = $previewer->getFileName($lc).".pdf"; - $filesize = filesize($file); - if(!($fh = @fopen($file, 'rb'))) { + if(!file_exists($file)) { $logger->log('Creating pdf preview failed', PEAR_LOG_ERR); - return $response->withJson('', 500); + return $this->renderer->json($response, array('success'=>false, 'message'=>'Creating pdf preview failed', 'data'=>''))->withStatus(500); } + $filesize = filesize($file); } } - $stream = new \Slim\Http\Stream($fh); // create a stream instance for the response body - + $response->getBody()->write(file_get_contents($file)); return $response->withHeader('Content-Type', $lc->getMimeType()) ->withHeader('Content-Description', 'File Transfer') ->withHeader('Content-Transfer-Encoding', 'binary') @@ -1200,8 +1220,7 @@ class SeedDMS_ExtPaperless_RestAPI_Controller { /* {{{ */ ->withHeader('Content-Length', $filesize) ->withHeader('Expires', '0') ->withHeader('Cache-Control', 'must-revalidate, post-check=0, pre-check=0') - ->withHeader('Pragma', 'no-cache') - ->withBody($stream); + ->withHeader('Pragma', 'no-cache'); } else { return $response->withStatus(403); } @@ -1243,8 +1262,8 @@ class SeedDMS_ExtPaperless_RestAPI_Controller { /* {{{ */ $filename = $document->getName().$lc->getFileType(); $file = $dms->contentDir . $lc->getPath(); - if(!($fh = @fopen($file, 'rb'))) { - return $response->withJson(array('success'=>false, 'message'=>'', 'data'=>''), 500); + if(!file_exists($file)) { + return $this->renderer->json($response, array('success'=>false, 'message'=>'', 'data'=>''))->withStatus(500); } $filesize = filesize($dms->contentDir . $lc->getPath()); } else { @@ -1257,19 +1276,18 @@ class SeedDMS_ExtPaperless_RestAPI_Controller { /* {{{ */ $previewer->createPreview($lc); if(!$previewer->hasPreview($lc)) { $logger->log('Creating pdf preview failed', PEAR_LOG_ERR); - return $response->withJson('', 500); + return $this->renderer->json($response, array('success'=>false, 'message'=>'Creating pdf preview failed', 'data'=>''))->withStatus(500); } else { $filename = $document->getName().".pdf"; $file = $previewer->getFileName($lc).".pdf"; - $filesize = filesize($file); - if(!($fh = @fopen($file, 'rb'))) { + if(!file_exists($file)) { $logger->log('Creating pdf preview failed', PEAR_LOG_ERR); - return $response->withJson('', 500); + return $this->renderer->json($response, array('success'=>false, 'message'=>'Creating pdf preview failed', 'data'=>''))->withStatus(500); } + $filesize = filesize($file); } } - $stream = new \Slim\Http\Stream($fh); // create a stream instance for the response body - + $response->getBody()->write(file_get_contents($file)); return $response->withHeader('Content-Type', $lc->getMimeType()) ->withHeader('Content-Description', 'File Transfer') ->withHeader('Content-Transfer-Encoding', 'binary') @@ -1277,8 +1295,7 @@ class SeedDMS_ExtPaperless_RestAPI_Controller { /* {{{ */ ->withHeader('Content-Length', $filesize) ->withHeader('Expires', '0') ->withHeader('Cache-Control', 'must-revalidate, post-check=0, pre-check=0') - ->withHeader('Pragma', 'no-cache') - ->withBody($stream); + ->withHeader('Pragma', 'no-cache'); } else { return $response->withStatus(403); } @@ -1304,7 +1321,7 @@ class SeedDMS_ExtPaperless_RestAPI_Controller { /* {{{ */ if($document->getAccessMode($userobj) >= M_READ) { $lc = $document->getLatestContent(); if($lc) { - return $response->withJson(array( + return $this->renderer->json($response, array( 'original_checksum'=>$lc->getChecksum(), 'original_size'=>(int) $lc->getFilesize(), 'original_mime_type'=>$lc->getMimeType(), @@ -1317,7 +1334,7 @@ class SeedDMS_ExtPaperless_RestAPI_Controller { /* {{{ */ 'lang'=>'en', 'archive_size'=>(int) $lc->getFilesize(), 'archive_metadata'=>[], - ), 200); + ))->withStatus(200); } else { return $response->withStatus(403); } @@ -1359,7 +1376,7 @@ class SeedDMS_ExtPaperless_RestAPI_Controller { /* {{{ */ $uploadedFiles = $request->getUploadedFiles(); if (count($uploadedFiles) == 0) { $logger->log('No files uploaded', PEAR_LOG_ERR); - return $response->withJson(getMLText("paperless_no_files_uploaded"), 400); + return $this->renderer->json($response, getMLText("paperless_no_files_uploaded"))->withStatus(400); } $file_info = array_pop($uploadedFiles); @@ -1367,7 +1384,7 @@ class SeedDMS_ExtPaperless_RestAPI_Controller { /* {{{ */ $maxuploadsize = SeedDMS_Core_File::parse_filesize($settings->_maxUploadSize); if ($maxuploadsize && $file_info->getSize() > $maxuploadsize) { $logger->log('File too large ('.$file_info->getSize().' > '.$maxuploadsize.')', PEAR_LOG_ERR); - return $response->withJson(getMLText("paperless_upload_maxsize"), 400); + return $this->renderer->json($response, getMLText("paperless_upload_maxsize"))->withStatus(400); } $origfilename = null; @@ -1382,7 +1399,7 @@ class SeedDMS_ExtPaperless_RestAPI_Controller { /* {{{ */ if(!$settings->_enableDuplicateDocNames) { if($mfolder->hasDocumentByName($docname)) { $logger->log('Duplicate document name '.$docname, PEAR_LOG_ERR); - return $response->withJson(getMLText("document_duplicate_name"), 409); + return $this->renderer->json($response, getMLText("document_duplicate_name"))->withStatus(409); } } /* If several tags are set, they will all be saved individually in @@ -1478,7 +1495,7 @@ class SeedDMS_ExtPaperless_RestAPI_Controller { /* {{{ */ $errmsg = $err; } $logger->log('Upload failed: '.$errmsg, PEAR_LOG_ERR); - return $response->withJson(getMLText('paperless_upload_failed'), 500); + return $this->renderer->json($response, getMLText('paperless_upload_failed'))->withStatus(500); } else { $logger->log('Upload succeeded', PEAR_LOG_INFO); /* Turn off for now, because file_info is not an array @@ -1495,10 +1512,11 @@ class SeedDMS_ExtPaperless_RestAPI_Controller { /* {{{ */ unlink($userfiletmp); } } - return $response->withJson('OK', 200); + $response->getBody()->write('OK'); + return $response->withStatus(200); } } - return $response->withJson(getMLText('paperless_missing_target_folder'), 400); + return $this->renderer->json($response, getMLText('paperless_missing_target_folder'))->withStatus(400); } /* }}} */ function patch_documents($request, $response, $args) { /* {{{ */ @@ -1601,7 +1619,7 @@ class SeedDMS_ExtPaperless_RestAPI_Controller { /* {{{ */ } } } - return $response->withJson($this->__getDocumentData($document), 200); + return $this->renderer->json($response, $this->__getDocumentData($document), 200); } /* }}} */ function delete_documents($request, $response, $args) { /* {{{ */ @@ -1656,8 +1674,11 @@ class SeedDMS_ExtPaperless_RestAPI_Auth { /* {{{ */ private $container; - public function __construct($container) { + private $responsefactory; + + public function __construct($container, $responsefactory) { $this->container = $container; + $this->responsefactory = $responsefactory; } /** @@ -1669,101 +1690,120 @@ class SeedDMS_ExtPaperless_RestAPI_Auth { /* {{{ */ * * @return \Psr\Http\Message\ResponseInterface */ - public function __invoke($request, $response, $next) { /* {{{ */ + public function __invoke($request, $handler) { /* {{{ */ // $this->container has the DI $dms = $this->container->get('dms'); $settings = $this->container->get('config'); $logger = $this->container->get('logger'); + $logger->log("Invoke paperless middleware for method ".$request->getMethod()." on '".$request->getUri()->getPath()."'", PEAR_LOG_INFO); + /* Skip this middleware if the authentication was already successful */ $userobj = null; if($this->container->has('userobj')) $userobj = $this->container->get('userobj'); - if($userobj) { - $response = $next($request, $response); - return $response; - } - - /* Pretent to be paperless ngx 1.10.0 with api version 2 */ - $response = $response->withHeader('x-api-version', '2')->withHeader('x-version', '1.13.0'); - - $logger->log("Invoke paperless middleware for method ".$request->getMethod()." on '".$request->getUri()->getPath()."'", PEAR_LOG_INFO); - if(!in_array($request->getUri()->getPath(), array('api/token/', 'api/'))) { - $userobj = null; - if(!empty($this->container->environment['HTTP_AUTHORIZATION'])) { - $tmp = explode(' ', $this->container->environment['HTTP_AUTHORIZATION'], 2); - switch($tmp[0]) { - case 'Token': - /* if jwtsecret is set, the token is expected to be a jwt */ - if(!empty($settings->_extensions['paperless']['jwtsecret'])) { - $token = new SeedDMS_JwtToken($settings->_extensions['paperless']['jwtsecret']); - if(!$tokenstr = $token->jwtDecode($tmp[1])) { - $logger->log("Could not decode jwt", PEAR_LOG_ERR); - return $response->withJson("Invalid token", 403); - } - $tmp = explode(':', json_decode($tokenstr, true)); - if($tmp[1] < time()) { - $logger->log("Jwt has expired at ".date('Y-m-d H:i:s', $tmp[1]), PEAR_LOG_ERR); - return $response->withJson(getMLText('paperless_token_has_expired'), 403); - } else { - $logger->log("Token is valid till ".date('Y-m-d H:i:s', $tmp[1]), PEAR_LOG_DEBUG); - } - if(!($userobj = $dms->getUser((int) $tmp[0]))) { - $logger->log("No such user ".$tmp[0], PEAR_LOG_ERR); - return $response->withJson("No such user", 403); - } - $dms->setUser($userobj); - if($this->container instanceof \Slim\Container) - $this->container['userobj'] = $userobj; - else - $this->container->set('userobj', $userobj); - $logger->log("Login with jwt as '".$userobj->getLogin()."' successful", PEAR_LOG_INFO); - } else { - if(!empty($settings->_apiKey) && !empty($settings->_apiUserId)) { - if($settings->_apiKey == $tmp[1]) { - if(!($userobj = $dms->getUser($settings->_apiUserId))) { - return $response->withStatus(403); - } + $environment = $request->getServerParams(); + $path = $environment['PATH_INFO'] ?? ''; + if(!$userobj) { + if(!in_array($path, array('/api/token/', '/api/'))) { + if(!empty($environment['HTTP_AUTHORIZATION'])) { + $tmp = explode(' ', $environment['HTTP_AUTHORIZATION'], 2); + switch($tmp[0]) { + case 'Token': + $logger->log("Token authentication with ".$tmp[0]."=".$tmp[1], PEAR_LOG_INFO); + /* if jwtsecret is set, the token is expected to be a jwt */ + if(!empty($settings->_extensions['paperless']['jwtsecret'])) { + $token = new SeedDMS_JwtToken($settings->_extensions['paperless']['jwtsecret']); + if(!$tokenstr = $token->jwtDecode($tmp[1])) { + $logger->log("Could not decode jwt", PEAR_LOG_ERR); + $response = $this->responsefactory->createResponse(); + return $this->renderer->json($response, array('success'=>false, 'message'=>'Could not decode token', 'data'=>''))->withStatus(403); + } + $tmp = explode(':', json_decode($tokenstr, true)); + if($tmp[1] < time()) { + $logger->log("Jwt has expired at ".date('Y-m-d H:i:s', $tmp[1]), PEAR_LOG_ERR); + $response = $this->responsefactory->createResponse(); + return $this->renderer->json($response, getMLText('paperless_token_has_expired'))->withStatus(403); } else { - $logger->log("Login with apikey '".$tmp[1]."' failed", PEAR_LOG_ERR); - return $response->withStatus(403); + $logger->log("Token is valid till ".date('Y-m-d H:i:s', $tmp[1]), PEAR_LOG_DEBUG); + } + if(!($userobj = $dms->getUser((int) $tmp[0]))) { + $logger->log("No such user ".$tmp[0], PEAR_LOG_ERR); + $response = $this->responsefactory->createResponse(); + return $this->renderer->json($response, array('success'=>false, 'message'=>'No such user', 'data'=>''))->withStatus(403); } $dms->setUser($userobj); if($this->container instanceof \Slim\Container) $this->container['userobj'] = $userobj; else $this->container->set('userobj', $userobj); - $logger->log("Login with apikey as '".$userobj->getLogin()."' successful", PEAR_LOG_INFO); + $logger->log("Login with jwt as '".$userobj->getLogin()."' successful", PEAR_LOG_INFO); + } else { + if(!empty($settings->_apiKey) && !empty($settings->_apiUserId)) { + if($settings->_apiKey == $tmp[1]) { + if(!($userobj = $dms->getUser($settings->_apiUserId))) { + $response = $this->responsefactory->createResponse(); + return $response->withStatus(403); + } + } else { + $logger->log("Login with apikey '".$tmp[1]."' failed", PEAR_LOG_ERR); + $response = $this->responsefactory->createResponse(); + return $response->withStatus(403); + } + $dms->setUser($userobj); + if($this->container instanceof \Slim\Container) + $this->container['userobj'] = $userobj; + else + $this->container->set('userobj', $userobj); + $logger->log("Login with apikey as '".$userobj->getLogin()."' successful", PEAR_LOG_INFO); + } } + break; + case 'Basic': + $logger->log("Basic authentication with ".$tmp[0]."=".$tmp[1], PEAR_LOG_INFO); + $authenticator = $this->container->get('authenticator'); + $kk = explode(':', base64_decode($tmp[1])); + $userobj = $authenticator->authenticate($kk[0], $kk[1]); + if(!$userobj) { + $logger->log("Login with basic authentication for '".$kk[0]."' failed", PEAR_LOG_ERR); + $response = $this->responsefactory->createResponse(); + return $response->withStatus(403); + } + $dms->setUser($userobj); + if($this->container instanceof \Slim\Container) + $this->container['userobj'] = $userobj; + else + $this->container->set('userobj', $userobj); + $logger->log("Login with basic authentication as '".$userobj->getLogin()."' successful", PEAR_LOG_INFO); + break; } - break; - case 'Basic': - $authenticator = $this->container->get('authenticator'); - $kk = explode(':', base64_decode($tmp[1])); - $userobj = $authenticator->authenticate($kk[0], $kk[1]); - if(!$userobj) { - $logger->log("Login with basic authentication for '".$kk[0]."' failed", PEAR_LOG_ERR); - return $response->withStatus(403); - } - $dms->setUser($userobj); - if($this->container instanceof \Slim\Container) - $this->container['userobj'] = $userobj; - else - $this->container->set('userobj', $userobj); - $logger->log("Login with basic authentication as '".$userobj->getLogin()."' successful", PEAR_LOG_INFO); - break; } + } else { + /* Set userobj to keep other middlewares for authentication from running */ + if($this->container instanceof \Slim\Container) + $this->container['userobj'] = true; + else + $this->container->set('userobj', true); } - } else { - /* Set userobj to keep other middlewares for authentication from running */ - if($this->container instanceof \Slim\Container) - $this->container['userobj'] = true; - else - $this->container->set('userobj', true); } + + if(!$userobj) + $logger->log("Not yet authenticated. Pass on to next middleware", PEAR_LOG_INFO); + else + $logger->log("Authenticated as ".(is_object($userobj) ? $userobj->getLogin() : "annon").". Pass on to next middleware", PEAR_LOG_INFO); + + /* Always pass on to the next middleware. If that middleware does + * authentication, then it should first check if 'userobj' in the container + * is already set. The authentication shipped with seeddms restapi does that + * and skips its own authentication, if userobj already exists. + */ + $response = $handler->handle($request); + + /* Pretent to be paperless ngx 1.13.0 with api version 2 */ + $response = $response->withHeader('x-api-version', '2')->withHeader('x-version', '1.13.0'); + $logger->log("End of paperless middleware for method ".$request->getMethod()." on '".$request->getUri()->getPath()."'", PEAR_LOG_INFO); - $response = $next($request, $response); return $response; } /* }}} */ } /* }}} */ @@ -1785,7 +1825,7 @@ class SeedDMS_ExtPaperless_RestAPI { /* {{{ */ */ public function addMiddleware($app) { /* {{{ */ $container = $app->getContainer(); - $app->add(new SeedDMS_ExtPaperless_RestAPI_Auth($container)); + $app->add(new SeedDMS_ExtPaperless_RestAPI_Auth($container, $app->getResponseFactory())); } /* }}} */ /**