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

Commit

Permalink
Refactor KeystoreAuthFrontend to use core service ApiKeysService.
Browse files Browse the repository at this point in the history
  • Loading branch information
cdujeu committed Aug 25, 2016
1 parent afe8e12 commit 9ef272c
Show file tree
Hide file tree
Showing 3 changed files with 204 additions and 83 deletions.
173 changes: 173 additions & 0 deletions core/src/core/src/pydio/Core/Services/ApiKeysService.php
Original file line number Original file line Diff line number Diff line change
@@ -0,0 +1,173 @@
<?php
/*
* Copyright 2007-2016 Abstrium <contact (at) pydio.com>
* This file is part of Pydio.
*
* Pydio is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Pydio is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Pydio. If not, see <http://www.gnu.org/licenses/>.
*
* The latest code can be found at <https://pydio.com/>.
*/
namespace Pydio\Core\Services;

use Pydio\Conf\Sql\SqlConfDriver;
use Pydio\Core\Exception\PydioException;
use Pydio\Core\Utils\Http\UserAgent;
use Pydio\Core\Utils\Vars\StringHelper;

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

/**
* Class ApiKeysService
* @package Pydio\Core\Services
*/
class ApiKeysService
{
/**
* @return SqlConfDriver
* @throws PydioException
*/
private static function getStore(){
$store = ConfService::getConfStorageImpl();
if (!($store instanceof SqlConfDriver)){
throw new PydioException("Wrong configuration driver, please use Sql");
}
return $store;
}

/**
* @param $userId
* @param string $deviceId
* @param string $deviceUA
* @param string $deviceIP
* @return array
* @throws PydioException
* @throws \Exception
*/
public static function generatePairForAuthfront($userId, $deviceId = "", $deviceUA = "", $deviceIP = ""){

$store = self::getStore();
$token = StringHelper::generateRandomString();
$private = StringHelper::generateRandomString();
$data = array("USER_ID" => $userId, "PRIVATE" => $private);
if (!empty($deviceId)) {
// Revoke previous tokens for this device
$cursor = null;
$keys = $store->simpleStoreList("keystore", $cursor, "", "serial", '%"DEVICE_ID";s:' . strlen($deviceId) . ':"' . $deviceId . '"%');
foreach ($keys as $keyId => $keyData) {
if ($keyData["USER_ID"] != $userId) continue;
$store->simpleStoreClear("keystore", $keyId);
}
$data["DEVICE_ID"] = $deviceId;
}
$data["DEVICE_UA"] = $deviceUA;
$data["DEVICE_IP"] = $deviceIP;
$store->simpleStoreSet("keystore", $token, $data, "serial");
return ["t" => $token, "p" => $private];

}

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

$store = self::getStore();
$token = StringHelper::generateRandomString();
$private = StringHelper::generateRandomString();
$data = [
"USER_ID" => $userId,
"PRIVATE" => $private,
"DEVICE_ID" => $deviceId
];
if(!empty($restrictToIP)){
$data["RESTRICT_TO_IP"] = $restrictToIP;
}
$store->simpleStoreSet("keystore", $token, $data, "serial");
return ["t" => $token, "p" => $private];

}

/**
* @param $token
* @param string $checkPrivate
* @return bool
* @throws PydioException
*/
public static function loadDataForPair($token, $checkPrivate = ""){
$data = null;
self::getStore()->simpleStoreGet("keystore", $token, "serial", $data);
if (empty($data)) {
return false;
}
if(!empty($checkPrivate)){
$p = $data["PRIVATE"];
unset($data["PRIVATE"]);
return $checkPrivate === $p ? $data : false;
}
return $data;
}

/**
* @param $userId
* @param $token
* @return int
* @throws PydioException
*/
public static function revokeTokens($userId, $token = ""){
$keys = self::getStore()->simpleStoreList("keystore", $cursor, $token, "serial", '%"USER_ID";s:' . strlen($userId) . ':"' . $userId . '"%');
$c = 0;
foreach ($keys as $keyId => $keyData) {
$c ++;
self::getStore()->simpleStoreClear("keystore", $keyId);
}
return $c;
}

/**
* @param $userId
* @return array
* @throws PydioException
*/
public static function listPairsForUser($userId){
$cursor = null;
$keys = self::getStore()->simpleStoreList("keystore", $cursor, "", "serial", '%"USER_ID";s:' . strlen($userId) . ':"' . $userId . '"%');
foreach ($keys as $keyId => &$keyData) {
unset($keyData["PRIVATE"]);
unset($keyData["USER_ID"]);
$deviceDesc = "Web Browser";
$deviceOS = "Unkown";
if (isSet($keyData["DEVICE_UA"])) {
$agent = $keyData["DEVICE_UA"];
if (strpos($agent, "python-requests") !== false) {
$deviceDesc = "PydioSync";
if (strpos($agent, "Darwin") !== false) $deviceOS = "Mac OS X";
else if (strpos($agent, "Windows/7") !== false) $deviceOS = "Windows 7";
else if (strpos($agent, "Windows/8") !== false) $deviceOS = "Windows 8";
else if (strpos($agent, "Linux") !== false) $deviceOS = "Linux";
} else {
$deviceOS = UserAgent::osFromUserAgent($agent);
}
}
$keyData["DEVICE_DESC"] = $deviceDesc;
$keyData["DEVICE_OS"] = $deviceOS;
}
return $keys;
}

}
2 changes: 1 addition & 1 deletion core/src/plugins/action.scheduler/manifest.xml
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
<clientForm id="scheduler_cronExpression"><![CDATA[ <clientForm id="scheduler_cronExpression"><![CDATA[
<div id="scheduler_cronExpression" box_width="420"> <div id="scheduler_cronExpression" box_width="420">
<div class="dialogLegend" ajxp_message_id="action.scheduler.21">AJXP_MESSAGE[action.scheduler.21]</div> <div class="dialogLegend" ajxp_message_id="action.scheduler.21">AJXP_MESSAGE[action.scheduler.21]</div>
<textarea class="dialogFocus" style="width:400px;height:80px; margin-top: 5px;" id="cron_expression"></textarea> <textarea class="dialogFocus" style="width:350px;height:80px; margin-top: 5px;" id="cron_expression"></textarea>
</div> </div>
]]></clientForm> ]]></clientForm>
<serverCallback methodName="switchAction" developerComment="Generate a correct expression to be inserted in Crontab, in order to trigger the {runAll} action on a regular basis."/> <serverCallback methodName="switchAction" developerComment="Generate a correct expression to be inserted in Crontab, in order to trigger the {runAll} action on a regular basis."/>
Expand Down
112 changes: 30 additions & 82 deletions core/src/plugins/authfront.keystore/KeystoreAuthFrontend.php
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -21,8 +21,12 @@
namespace Pydio\Auth\Frontend; namespace Pydio\Auth\Frontend;


use Exception; use Exception;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Pydio\Core\Exception\PydioException;
use Pydio\Core\Model\Context; use Pydio\Core\Model\Context;
use Pydio\Core\Model\ContextInterface; use Pydio\Core\Model\ContextInterface;
use Pydio\Core\Services\ApiKeysService;
use Pydio\Core\Services\AuthService; use Pydio\Core\Services\AuthService;
use Pydio\Auth\Frontend\Core\AbstractAuthFrontend; use Pydio\Auth\Frontend\Core\AbstractAuthFrontend;
use Pydio\Core\Services\ConfService; use Pydio\Core\Services\ConfService;
Expand All @@ -32,6 +36,7 @@
use Pydio\Core\Utils\Vars\StringHelper; use Pydio\Core\Utils\Vars\StringHelper;
use Pydio\Core\Utils\Http\UserAgent; use Pydio\Core\Utils\Http\UserAgent;
use Pydio\Core\Controller\HTMLWriter; use Pydio\Core\Controller\HTMLWriter;
use Zend\Diactoros\Response\JsonResponse;


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


Expand Down Expand Up @@ -74,16 +79,12 @@ function tryToLogUser(\Psr\Http\Message\ServerRequestInterface &$request, \Psr\H
//$this->logDebug(__FUNCTION__, "Empty token", $_POST); //$this->logDebug(__FUNCTION__, "Empty token", $_POST);
return false; return false;
} }
$this->storage = ConfService::getConfStorageImpl();
if (!($this->storage instanceof \Pydio\Conf\Sql\SqlConfDriver)) return false;


$data = null; $data = ApiKeysService::loadDataForPair($token);
$this->storage->simpleStoreGet("keystore", $token, "serial", $data); if($data === false){
if (empty($data)) { $this->logDebug(__FUNCTION__, "Cannot find token in keystore");
//$this->logDebug(__FUNCTION__, "Cannot find token in keystore");
return false; return false;
} }
//$this->logDebug(__FUNCTION__, "Found token in keystore");
$userId = $data["USER_ID"]; $userId = $data["USER_ID"];
$private = $data["PRIVATE"]; $private = $data["PRIVATE"];
$explode = explode("?", $_SERVER["REQUEST_URI"]); $explode = explode("?", $_SERVER["REQUEST_URI"]);
Expand Down Expand Up @@ -118,31 +119,24 @@ function tryToLogUser(\Psr\Http\Message\ServerRequestInterface &$request, \Psr\H
*/ */
public function revokeUserTokens(ContextInterface $ctx, $userId) public function revokeUserTokens(ContextInterface $ctx, $userId)
{ {

try{
$this->storage = ConfService::getConfStorageImpl(); $count = ApiKeysService::revokeTokens($userId);
if (!($this->storage instanceof \Pydio\Conf\Sql\SqlConfDriver)) return false; $this->logInfo(__FUNCTION__, "Revoking " . $count . " keys for user '" . $userId . "' on user modification action.");
$cursor = null; }catch (PydioException $e){}
$keys = $this->storage->simpleStoreList("keystore", $cursor, "", "serial", '%"USER_ID";s:' . strlen($userId) . ':"' . $userId . '"%');
foreach ($keys as $keyId => $keyData) {
$this->storage->simpleStoreClear("keystore", $keyId);
}
if (count($keys)) {
$this->logInfo(__FUNCTION__, "Revoking " . count($keys) . " keys for user '" . $userId . "' on password change action.");
}
return null;
} }


/** /**
* @param String $action * @param ServerRequestInterface $requestInterface
* @param array $httpVars * @param ResponseInterface $responseInterface
* @param array $fileVars
* @param ContextInterface $ctx
* @return String * @return String
* @throws Exception
*/ */
function authTokenActions($action, $httpVars, $fileVars, ContextInterface $ctx) function authTokenActions(ServerRequestInterface $requestInterface, ResponseInterface &$responseInterface)
{ {

/** @var ContextInterface $ctx */
$ctx = $requestInterface->getAttribute("ctx");
$action = $requestInterface->getAttribute("action");
$httpVars = $requestInterface->getParsedBody();

if (!$ctx->hasUser()) { if (!$ctx->hasUser()) {
return null; return null;
} }
Expand All @@ -164,29 +158,9 @@ function authTokenActions($action, $httpVars, $fileVars, ContextInterface $ctx)
echo ""; echo "";
break; break;
} }

$device = (isSet($httpVars["device"]) ? InputFilter::sanitize($httpVars["device"], InputFilter::SANITIZE_ALPHANUM) : "");
$token = StringHelper::generateRandomString(); $tokenPair = ApiKeysService::generatePairForAuthfront($user, $device, $_SERVER["HTTP_USER_AGENT"], $_SERVER["REMOTE_ADDR"]);
$private = StringHelper::generateRandomString(); $responseInterface = new JsonResponse($tokenPair);
$data = array("USER_ID" => $user, "PRIVATE" => $private);
if (!empty($httpVars["device"])) {
// Revoke previous tokens for this device
$device = $httpVars["device"];
$cursor = null;
$keys = $this->storage->simpleStoreList("keystore", $cursor, "", "serial", '%"DEVICE_ID";s:' . strlen($device) . ':"' . $device . '"%');
foreach ($keys as $keyId => $keyData) {
if ($keyData["USER_ID"] != $user) continue;
$this->storage->simpleStoreClear("keystore", $keyId);
}
$data["DEVICE_ID"] = $device;
}
$data["DEVICE_UA"] = $_SERVER['HTTP_USER_AGENT'];
$data["DEVICE_IP"] = $_SERVER['REMOTE_ADDR'];
$this->storage->simpleStoreSet("keystore", $token, $data, "serial");
HTMLWriter::charsetHeader("application/json");
echo(json_encode(array(
"t" => $token,
"p" => $private)
));


break; break;


Expand All @@ -196,45 +170,19 @@ function authTokenActions($action, $httpVars, $fileVars, ContextInterface $ctx)
$mess = LocaleService::getMessages(); $mess = LocaleService::getMessages();
$passedKeyId = ""; $passedKeyId = "";
if (isSet($httpVars["key_id"])) $passedKeyId = $httpVars["key_id"]; if (isSet($httpVars["key_id"])) $passedKeyId = $httpVars["key_id"];
$cursor = null; $r = ApiKeysService::revokeTokens($user, $passedKeyId);
$keys = $this->storage->simpleStoreList("keystore", $cursor, $passedKeyId, "serial", '%"USER_ID";s:' . strlen($user) . ':"' . $user . '"%'); $responseInterface = new JsonResponse([
foreach ($keys as $keyId => $keyData) {
$this->storage->simpleStoreClear("keystore", $keyId);
}
$message = array(
"result" => "SUCCESS", "result" => "SUCCESS",
"message" => $mess["keystore.8"] "message" => $mess["keystore.8"]
); ]);
HTMLWriter::charsetHeader("application/json");
echo json_encode($message);
break; break;


case "keystore_list_tokens": case "keystore_list_tokens":

if (!isSet($user)) break; if (!isSet($user)) break;
$cursor = null;
$keys = $this->storage->simpleStoreList("keystore", $cursor, "", "serial", '%"USER_ID";s:' . strlen($user) . ':"' . $user . '"%'); $keys = ApiKeysService::listPairsForUser($user);
foreach ($keys as $keyId => &$keyData) { $responseInterface = new JsonResponse($keys);
unset($keyData["PRIVATE"]);
unset($keyData["USER_ID"]);
$deviceDesc = "Web Browser";
$deviceOS = "Unkown";
if (isSet($keyData["DEVICE_UA"])) {
$agent = $keyData["DEVICE_UA"];
if (strpos($agent, "python-requests") !== false) {
$deviceDesc = "PydioSync";
if (strpos($agent, "Darwin") !== false) $deviceOS = "Mac OS X";
else if (strpos($agent, "Windows/7") !== false) $deviceOS = "Windows 7";
else if (strpos($agent, "Windows/8") !== false) $deviceOS = "Windows 8";
else if (strpos($agent, "Linux") !== false) $deviceOS = "Linux";
} else {
$deviceOS = UserAgent::osFromUserAgent($agent);
}
}
$keyData["DEVICE_DESC"] = $deviceDesc;
$keyData["DEVICE_OS"] = $deviceOS;
}
header("Content-type: application/json;");
echo json_encode($keys);


break; break;


Expand Down

0 comments on commit 9ef272c

Please sign in to comment.