Skip to content
This repository has been archived by the owner on Nov 25, 2020. It is now read-only.

Commit

Permalink
Protect request-options queries with a unique api key.
Browse files Browse the repository at this point in the history
  • Loading branch information
cdujeu committed Aug 26, 2016
1 parent ad6a2e8 commit fdfad76
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 14 deletions.
Expand Up @@ -24,6 +24,8 @@
use Pydio\Access\Core\Model\AJXP_Node;
use Pydio\Access\Driver\StreamProvider\FS\FsAccessWrapper;
use Pydio\Core\Controller\HTMLWriter;
use Pydio\Core\Services\ApiKeysService;
use Pydio\Core\Services\AuthService;
use Pydio\Core\Services\ConfService;
use Pydio\Core\Utils\ApplicationState;
use Pydio\Core\Utils\FileHelper;
Expand Down Expand Up @@ -440,7 +442,8 @@ protected function sendToAccelerator($accelConfiguration, $localPathOrNode, $ser
}

// Pydio Agent acceleration - We make sure that request was really proxied by Agent, by checking a specific header.
if($accelConfiguration === "pydio" && array_key_exists("HTTP_X_PYDIO_DOWNLOAD_SUPPORTED", $serverParams)) {
if($accelConfiguration === "pydio" && array_key_exists("HTTP_X_PYDIO_DOWNLOAD_SUPPORTED", $serverParams)
&& ApiKeysService::requestHasValidHeadersForAdminTask($serverParams, "go-upload", AuthService::getLoggedUser()->getId())) {

if ($localPathOrNode instanceof AJXP_Node) {
$options = MetaStreamWrapper::getResolvedOptionsForNode($localPathOrNode);
Expand Down
52 changes: 47 additions & 5 deletions core/src/core/src/pydio/Core/Services/ApiKeysService.php
Expand Up @@ -20,10 +20,13 @@
*/
namespace Pydio\Core\Services;

use Psr\Http\Message\ServerRequestInterface;
use Pydio\Conf\Sql\SqlConfDriver;
use Pydio\Core\Exception\AuthRequiredException;
use Pydio\Core\Exception\PydioException;
use Pydio\Core\Utils\Http\UserAgent;
use Pydio\Core\Utils\Vars\StringHelper;
use Pydio\Log\Core\Logger;

defined('AJXP_EXEC') or die('Access not allowed');

Expand Down Expand Up @@ -79,21 +82,21 @@ public static function generatePairForAuthfront($userId, $deviceId = "", $device

/**
* @param $userId
* @param $deviceId
* @param $adminTaskId
* @param string $restrictToIP
* @return array
* @throws PydioException
* @throws \Exception
*/
public static function generatePairForAdminTasks($userId, $deviceId, $restrictToIP = ""){
public static function generatePairForAdminTask($adminTaskId, $userId, $restrictToIP = ""){

$store = self::getStore();
$token = StringHelper::generateRandomString();
$private = StringHelper::generateRandomString();
$data = [
"USER_ID" => $userId,
"PRIVATE" => $private,
"DEVICE_ID" => $deviceId
"USER_ID" => $userId,
"PRIVATE" => $private,
"ADMIN_TASK_ID" => $adminTaskId
];
if(!empty($restrictToIP)){
$data["RESTRICT_TO_IP"] = $restrictToIP;
Expand All @@ -103,6 +106,45 @@ public static function generatePairForAdminTasks($userId, $deviceId, $restrictTo

}

/**
* @param $adminTaskId
* @param $userId
* @return array|null
* @throws PydioException
*/
public static function findPairForAdminTask($adminTaskId, $userId){

$keys = self::getStore()->simpleStoreList("keystore", $cursor, "", "serial", '%"ADMIN_TASK_ID";s:' . strlen($adminTaskId) . ':"' . $adminTaskId . '"%');
foreach($keys as $kId => $kData){
if($kData["USER_ID"] === $userId){
return ["t" => $kId, "p" => $kData["PRIVATE"]];
}
}
return null;

}

/**
* @param $serverData
* @param $adminTaskId
* @param $userId
* @return bool
*/
public static function requestHasValidHeadersForAdminTask($serverData, $adminTaskId, $userId){
if(!isSet($serverData['HTTP_X_PYDIO_ADMIN_AUTH'])){
Logger::error(__CLASS__, __FUNCTION__,"Invalid tokens for admin task $adminTaskId");
return false;
}
list($t, $p) = explode(":", trim($serverData['HTTP_X_PYDIO_ADMIN_AUTH']));
$existingKey = self::findPairForAdminTask("go-upload", $userId);
if($existingKey === null || $existingKey['p'] !== $p || $existingKey['t'] !== $t){
Logger::error(__CLASS__, __FUNCTION__, "Invalid tokens for admin task $adminTaskId");
return false;
}
Logger::debug(__CLASS__, "Valid tokens for admin task $adminTaskId");
return true;
}

/**
* @param $token
* @param string $checkPrivate
Expand Down
9 changes: 1 addition & 8 deletions core/src/plugins/authfront.keystore/KeystoreAuthFrontend.php
Expand Up @@ -46,12 +46,7 @@
*/
class KeystoreAuthFrontend extends AbstractAuthFrontend
{

/**
* @var SqlConfDriver $storage
*/
var $storage;


/**
* @param $httpVars
* @param $varName
Expand Down Expand Up @@ -140,8 +135,6 @@ function authTokenActions(ServerRequestInterface $requestInterface, ResponseInte
if (!$ctx->hasUser()) {
return null;
}
$this->storage = ConfService::getConfStorageImpl();
if (!($this->storage instanceof \Pydio\Conf\Sql\SqlConfDriver)) return false;

$u = $ctx->getUser();
$user = $u->getId();
Expand Down
9 changes: 9 additions & 0 deletions core/src/plugins/core.mq/MqManager.php
Expand Up @@ -34,6 +34,7 @@
use Pydio\Core\Model\ContextInterface;
use Pydio\Core\PluginFramework\PluginsService;
use Pydio\Core\Serializer\UserXML;
use Pydio\Core\Services\ApiKeysService;
use Pydio\Core\Services\AuthService;
use Pydio\Core\Services\ConfService;
use Pydio\Core\Services\RepositoryService;
Expand Down Expand Up @@ -536,6 +537,13 @@ public function generateCaddyFile($params) {
$secure = $params["UPLOAD_SECURE"];
$path = "/" . trim($params["UPLOAD_PATH"], "/");

// WE SHOULD HAVE A CONTEXT AT THIS POINT, INSTEAD OF CALLING ::getLoggedUser()
$adminKey = ApiKeysService::findPairForAdminTask("go-upload", AuthService::getLoggedUser()->getId());
if($adminKey === null){
$adminKey = ApiKeysService::generatePairForAdminTask("go-upload", AuthService::getLoggedUser()->getId(), $host);
}
$adminKeyString = $adminKey["t"].":".$adminKey["p"];

$key = "http" . ($secure ? "s" : "") . "://" . $host . ":" . $port;
$hosts[$key] = array_merge(
(array)$hosts[$key],
Expand All @@ -551,6 +559,7 @@ public function generateCaddyFile($params) {
"pydioauth " . $path => [$tokenURL . "&device=upload"],
"pydiopre " . $path => [$authURL, "{\n" .
"\t\theader X-File-Direct-Upload request-options\n" .
"\t\theader X-Pydio-Admin-Auth $adminKeyString\n" .
"\t}"
],
"pydioupload " . $path => [],
Expand Down
14 changes: 14 additions & 0 deletions core/src/plugins/uploader.html/SimpleUpload.php
Expand Up @@ -23,7 +23,10 @@
use Pydio\Access\Core\Model\AJXP_Node;
use Pydio\Access\Core\Model\UserSelection;
use Pydio\Core\Controller\Controller;
use Pydio\Core\Exception\AuthRequiredException;
use Pydio\Core\Exception\PydioException;
use Pydio\Core\Model\ContextInterface;
use Pydio\Core\Services\ApiKeysService;
use Pydio\Core\Services\LocaleService;
use Pydio\Core\Utils\Vars\InputFilter;
use Pydio\Core\Http\Message\ExternalUploadedFile;
Expand Down Expand Up @@ -126,6 +129,17 @@ public function preProcess(\Psr\Http\Message\ServerRequestInterface &$request, \
throw new PydioException("Unrecognized direct upload status ". $externalUploadStatus);
}

if($externalUploadStatus === ExternalUploadedFile::STATUS_REQUEST_OPTIONS){

/** @var ContextInterface $ctx */
$ctx = $request->getAttribute("ctx");
$uId = $ctx->getUser()->getId();
if(!ApiKeysService::requestHasValidHeadersForAdminTask($request->getServerParams(), "go-upload", $uId)){
throw new AuthRequiredException();
}

}

$uploadedFile = new ExternalUploadedFile($externalUploadStatus, $fileSizeH, $fileNameH);

} else {
Expand Down

0 comments on commit fdfad76

Please sign in to comment.