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

Commit

Permalink
Rework CLI integration to use nice Symfony/Console features (tables, …
Browse files Browse the repository at this point in the history
…coloration).

Pass pretty printing option context for json and xml format in command line.
  • Loading branch information
cdujeu committed Jul 11, 2016
1 parent 67f291c commit 68008db
Show file tree
Hide file tree
Showing 7 changed files with 442 additions and 137 deletions.
236 changes: 154 additions & 82 deletions core/src/core/src/pydio/Core/Http/Cli/AuthCliMiddleware.php
Expand Up @@ -26,6 +26,7 @@

use Pydio\Core\Exception\AuthRequiredException;
use Pydio\Core\Exception\LoginException;
use Pydio\Core\Http\Response\SerializableResponseStream;
use Pydio\Core\Http\Server;
use Pydio\Core\Model\Context;
use Pydio\Core\PluginFramework\PluginsService;
Expand All @@ -38,7 +39,9 @@
use Pydio\Core\Utils\TextEncoder;
use Pydio\Core\Utils\Utils;
use Pydio\Log\Core\Logger;
use Symfony\Component\Console\Output\OutputInterface;
use Zend\Diactoros\Response;
use Zend\Diactoros\Stream;

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

Expand All @@ -48,6 +51,57 @@
*/
class AuthCliMiddleware
{

/**
* @param $options
* @return \Pydio\Core\Model\UserInterface
* @throws AuthRequiredException
*/
protected static function authenticateFromCliParameters($options){

$optUser = $options["u"];
$optPass = $options["p"];

if (!empty($options["p"])) {

$optPass = $options["p"];

} else {
// Consider "u" is a crypted version of u:p
$optToken = $options["t"];
$cKey = ConfService::getGlobalConf("AJXP_CLI_SECRET_KEY", "conf");
if(empty($cKey)) $cKey = "\1CDAFx¨op#";
$optUser = trim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, md5($optToken.$cKey), base64_decode($optUser), MCRYPT_MODE_ECB), "\0");
$env = getenv("AJXP_SAFE_CREDENTIALS");
if(!empty($env)){
$array = MemorySafe::getCredentialsFromEncodedString($env);
if(isSet($array["user"]) && $array["user"] == $optUser){
unset($optToken);
$optPass = $array["password"];
}
}

}


if (UsersService::usersEnabled() && !empty($optUser)) {
$seed = AuthService::generateSeed();
if ($seed != -1) {
$optPass = md5(md5($optPass).$seed);
}
try{
$loggedUser = AuthService::logUser($optUser, $optPass, isSet($optToken), false, $seed);
}catch (LoginException $l){
throw new AuthRequiredException();
}

} else {
throw new AuthRequiredException();
}

return $loggedUser;
}

/**
* @param ServerRequestInterface $requestInterface
* @param \Psr\Http\Message\ResponseInterface $responseInterface
Expand All @@ -60,19 +114,11 @@ public static function handleRequest(ServerRequestInterface $requestInterface, R
$driverImpl = ConfService::getAuthDriverImpl();
PluginsService::getInstance(Context::emptyContext())->setPluginUniqueActiveForType("auth", $driverImpl->getName(), $driverImpl);

/** @var OutputInterface $output */
$output = $requestInterface->getAttribute("cli-output");
$options = $requestInterface->getAttribute("cli-options");
$optUser = $options["u"];
$optPass = $options["p"];
$optRepoId = $options["r"];

$repository = RepositoryService::getRepositoryById($optRepoId);
if ($repository == null) {
$repository = RepositoryService::getRepositoryByAlias($optRepoId);
if ($repository != null) {
$optRepoId =($repository->isWriteable()?$repository->getUniqueId():$repository->getId());
}
}

$impersonateUsers = false;
// TODO 1/ REIMPLEMENT parameter queue: to pass a file with many user names?
/**
Expand Down Expand Up @@ -117,100 +163,126 @@ public static function handleRequest(ServerRequestInterface $requestInterface, R
//}


$loggedUser = self::authenticateFromCliParameters($options);

if(!empty($options["i"])){
if(!is_array($options["i"])) $options["i"] = [$options["i"]];
$impersonateUsers = $options["i"];
}
$requestInterface = $requestInterface->withAttribute("action", $options["a"]);

if(UsersService::usersEnabled() && ApplicationState::detectApplicationFirstRun()){

RolesService::bootSequence();

if (!empty($options["p"])) {
$optPass = $options["p"];
} else {
// Consider "u" is a crypted version of u:p
$optToken = $options["t"];
$cKey = ConfService::getGlobalConf("AJXP_CLI_SECRET_KEY", "conf");
if(empty($cKey)) $cKey = "\1CDAFx¨op#";
$optUser = trim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, md5($optToken.$cKey), base64_decode($optUser), MCRYPT_MODE_ECB), "\0");
$env = getenv("AJXP_SAFE_CREDENTIALS");
if(!empty($env)){
$array = MemorySafe::getCredentialsFromEncodedString($env);
if(isSet($array["user"]) && $array["user"] == $optUser){
unset($optToken);
$optPass = $array["password"];
}
}
}

// Output directly to STDOUT
$responseInterface = $responseInterface->withBody(new Stream(STDOUT, "w"));

if (UsersService::usersEnabled() && !empty($optUser)) {
$seed = AuthService::generateSeed();
if ($seed != -1) {
$optPass = md5(md5($optPass).$seed);
}
$applyCallback = function ($userId, $baseGroup, $index, $total) use ($optRepoId, $requestInterface, $output, $next){

$actionName = $requestInterface->getAttribute("action");
$output->writeln("<info>*****************************</info>");
$output->writeln("<info>Current User is '".$userId."'</info>");
$output->writeln("<info>*****************************</info>");
try{
$loggedUser = AuthService::logUser($optUser, $optPass, isSet($optToken), false, $seed);
}catch (LoginException $l){
throw new AuthRequiredException();
$user = UsersService::getUserById($userId);

if(is_array($optRepoId)){
$repos = [];
foreach($optRepoId as $repoId){
$repoObj = UsersService::getRepositoryWithPermission($user, $repoId);
$repos[$repoObj->getId()] = $repoObj;
}
}else if($optRepoId === "*"){

$repos = UsersService::getRepositoriesForUser($user, false); // Do not include shared
// Exclude "ajxp_*" repositories and "inbox"
foreach($repos as $id=>$object){
if($id === "inbox" || strpos($id, "ajxp_") === 0) unset($repos[$id]);
}

}else{
$repoObject = UsersService::getRepositoryWithPermission($user, $optRepoId);
$repos = [$repoObject->getId() => $repoObject];
}

foreach( $repos as $repoId => $repositoryInterface ){

try{
$output->writeln("<comment>Applying action '$actionName' on workspace ".$repositoryInterface->getDisplay()." (".$repoId.")</comment>");
$subResponse = new Response();
$ctx = Context::contextWithObjects($user, $repositoryInterface);
$requestInterface = $requestInterface->withAttribute("ctx", $ctx);
Logger::updateContext($ctx);
TextEncoder::updateContext($ctx);

$subResponse = Server::callNextMiddleWareAndRewind(function($middleware){
return (is_array($middleware) && $middleware["0"] == "Pydio\\Core\\Http\\Cli\\AuthCliMiddleware" && $middleware[1] == "handleRequest");
},
$requestInterface,
$subResponse,
$next
);

$body = $subResponse->getBody();
if($body instanceof SerializableResponseStream){
if($requestInterface->getParsedBody()["format"] == "json"){
$body->setSerializer(SerializableResponseStream::SERIALIZER_TYPE_JSON, ["pretty" => true]);
}else if($requestInterface->getParsedBody()["format"] == "xml"){
$body->setSerializer(SerializableResponseStream::SERIALIZER_TYPE_XML, ["pretty" => true]);
}else if($body->supportsSerializer(SerializableResponseStream::SERIALIZER_TYPE_CLI)){
$body->setSerializer(SerializableResponseStream::SERIALIZER_TYPE_CLI, ["output" => $output]);
}
}
$output->writeln( $body->getContents() );

}catch (\Exception $repoEx){

$output->writeln("<error>".$repoEx->getMessage()."</error>");

}

}

}catch (\Exception $userEx){

$output->writeln("<error>".$userEx->getMessage()."</error>");

}

} else {
throw new AuthRequiredException();
}
};

$requestInterface = $requestInterface->withAttribute("action", $options["a"]);

if(UsersService::usersEnabled() && ApplicationState::detectApplicationFirstRun()){
RolesService::bootSequence();
if(!empty($options["i"])){
if(!is_array($options["i"]) && strpos($options["i"], "*") === false){
$options["i"] = [$options["i"]];
}
$impersonateUsers = $options["i"];
}

if ($impersonateUsers !== false && $loggedUser->isAdmin()) {

foreach ($impersonateUsers as $impersonateUser){
AuthService::disconnect();
$responseInterface->getBody()->write("\n--- Impersonating user ".$impersonateUser);
try{
$loggedUser = AuthService::logUser($impersonateUser, "empty", true, false, "");
//ConfService::switchRootDir($optRepoId, true);
//Controller::registryReset();
$subResponse = new Response();
$ctx = new Context();
$ctx->setUserObject($loggedUser);
$ctx->setRepositoryId($optRepoId);
$requestInterface = $requestInterface->withAttribute("ctx", $ctx);
Logger::updateContext($ctx);

$subResponse = Server::callNextMiddleWareAndRewind(function($middleware){
return (is_array($middleware) && $middleware["0"] == "Pydio\\Core\\Http\\Cli\\AuthCliMiddleware" && $middleware[1] == "handleRequest");
},
$requestInterface,
$subResponse,
$next
);
$responseInterface->getBody()->write("\n". $subResponse->getBody());

}catch (\Exception $e){

$responseInterface->getBody()->write("\nERROR: ".$e->getMessage());

if(is_array($impersonateUsers)){
// Apply to a predefined list of users
foreach ($impersonateUsers as $index => $impersonateUser) {
$applyCallback($impersonateUser, "/", $index, count($impersonateUsers));
}
}
return $responseInterface;

}else{
}else if($impersonateUsers === "*"){
// Apply to all users of current group
UsersService::browseUsersGroupsWithCallback($loggedUser->getGroupPath(), $applyCallback, false);

$repoObject = UsersService::getRepositoryWithPermission($loggedUser, $optRepoId);
}else if($impersonateUsers === "**/*"){
// Apply to all users recursively
UsersService::browseUsersGroupsWithCallback($loggedUser->getGroupPath(), $applyCallback, true);

$ctx = new Context();
$ctx->setUserObject($loggedUser);
$ctx->setRepositoryObject($repoObject);
$requestInterface = $requestInterface->withAttribute("ctx", $ctx);
Logger::updateContext($ctx);
TextEncoder::updateContext($ctx);
}

}else{

return Server::callNextMiddleWare($requestInterface, $responseInterface, $next);
$applyCallback($loggedUser->getId(), "/", 1, 1);

}

return $responseInterface;

}

Expand Down
25 changes: 12 additions & 13 deletions core/src/core/src/pydio/Core/Http/Cli/CliMiddleware.php
Expand Up @@ -34,7 +34,11 @@

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


/**
* Class CliMiddleware
* Dedicated Middleware for interacting with Symfony Command
* @package Pydio\Core\Http\Cli
*/
class CliMiddleware implements ITopLevelMiddleware
{
/**
Expand Down Expand Up @@ -62,7 +66,7 @@ public function handleRequest(ServerRequestInterface $requestInterface, Response

} catch (AuthRequiredException $e){

$output->writeln("ERROR:Authentication Failed.");
$output->writeln("<error>Authentication Failed</error>");
if($statusFile !== false){
file_put_contents($statusFile, "ERROR:Authentication Failed.");
}
Expand All @@ -72,7 +76,7 @@ public function handleRequest(ServerRequestInterface $requestInterface, Response

} catch (PydioException $e){

$output->writeln("ERROR:".$e->getMessage());
$output->writeln("<error>".$e->getMessage()."</error>");
if($statusFile !== false){
file_put_contents($statusFile, "ERROR:".$e->getMessage());
}
Expand All @@ -82,7 +86,7 @@ public function handleRequest(ServerRequestInterface $requestInterface, Response

} catch (\Exception $e){

$output->writeln("ERROR:".$e->getMessage());
$output->writeln("<error>".$e->getMessage()."</error>");
if($statusFile !== false){
file_put_contents($statusFile, "ERROR:".$e->getMessage());
}
Expand All @@ -96,23 +100,18 @@ public function handleRequest(ServerRequestInterface $requestInterface, Response

public function emitResponse(ServerRequestInterface $requestInterface, ResponseInterface $responseInterface){

$output = $requestInterface->getAttribute("cli-output");
if($responseInterface !== false && $responseInterface->getBody() && $responseInterface->getBody() instanceof SerializableResponseStream){
// For the moment, use XML by default
// Todo: Create A CLI Serializer for pretty printing?
if($requestInterface->getParsedBody()["format"] == "json"){
$responseInterface->getBody()->setSerializer(SerializableResponseStream::SERIALIZER_TYPE_JSON);
}
}
$body = "".$responseInterface->getBody();
$output->writeln("");
$output->writeln("-----------------------------------");
$output->writeln("Executing Action" . $requestInterface->getAttribute("action"));
$output->writeln("-----------------------------------");
if(!empty($body)){
$output->writeln("" . $responseInterface->getBody());
$output = $requestInterface->getAttribute("cli-output");
if(!empty($output)){
$output->writeln("".$responseInterface->getBody());
}else{
$output->writeln("No output");
echo "".$responseInterface->getBody();
}

}
Expand Down

0 comments on commit 68008db

Please sign in to comment.