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

Commit

Permalink
Merge remote-tracking branch 'upstream/develop' into 2011
Browse files Browse the repository at this point in the history
  • Loading branch information
c12simple committed Nov 25, 2014
2 parents 83e640d + 2117662 commit b6f9e98
Show file tree
Hide file tree
Showing 12 changed files with 277 additions and 16 deletions.
3 changes: 0 additions & 3 deletions core/src/core/classes/class.AJXP_Utils.php
Expand Up @@ -1622,9 +1622,6 @@ public static function parseStandardFormParameters(&$repDef, &$options, $userId
$options[substr($key, strlen($prefix))] = $value;
unset($repDef[$key]);
} else {
if ($key == "DISPLAY") {
$value = SystemTextEncoding::fromUTF8(AJXP_Utils::securePath($value));
}
$repDef[$key] = $value;
}
}
Expand Down
10 changes: 4 additions & 6 deletions core/src/core/classes/class.AuthService.php
Expand Up @@ -370,15 +370,13 @@ public static function logUser($user_id, $pwd, $bypass_pwd = false, $cookieLogin
if ($authDriver->isAjxpAdmin($user_id)) {
$user->setAdmin(true);
}
if(self::$useSession) $_SESSION["AJXP_USER"] = $user;
else self::$currentUser = $user;

if ($user->isAdmin()) {
$user = self::updateAdminRights($user);
} else {
if (!$user->hasParent() && $user_id != "guest") {
//$user->setAcl("ajxp_shared", "rw");
}
self::updateUser($user);
}
if(self::$useSession) $_SESSION["AJXP_USER"] = $user;
else self::$currentUser = $user;

if ($authDriver->autoCreateUser() && !$user->storageExists()) {
$user->save("superuser"); // make sure update rights now
Expand Down
5 changes: 5 additions & 0 deletions core/src/core/classes/class.ConfService.php
Expand Up @@ -215,6 +215,11 @@ public static function getAuthDriverImpl()
return AJXP_PluginsService::getInstance()->getPluginById("core.auth")->getAuthImpl();
}

/**
* @param AbstractAjxpUser $loggedUser
* @param String|int $parameterId
* @return bool
*/
public static function switchUserToActiveRepository($loggedUser, $parameterId = -1)
{
if (isSet($_SESSION["PENDING_REPOSITORY_ID"]) && isSet($_SESSION["PENDING_FOLDER"])) {
Expand Down
30 changes: 30 additions & 0 deletions core/src/core/classes/class.SystemTextEncoding.php
Expand Up @@ -163,4 +163,34 @@ public static function isUtf8($string)
| \xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16
)*$%xs', $string);
}
/**
* Transform a string from current Storage charset to utf8
* @static
* @param string $filesystemElement
* @param bool $test Test if it's already UTF8 or not, to avoid double-encoding
* @return string
*/
public static function fromStorageEncoding($filesystemElement, $test = true)
{
if ($test && SystemTextEncoding::isUtf8($filesystemElement)) {
return $filesystemElement;
}
$enc = SystemTextEncoding::getEncoding();
return SystemTextEncoding::changeCharset($enc, "UTF-8", $filesystemElement);
}
/**
* Decode a string from UTF8 to current Storage Charset
* @static
* @param string $filesystemElement
* @param bool $test Try to detect if it's really utf8 or not
* @return string
*/
public static function toStorageEncoding($filesystemElement, $test = false)
{
if ($test && !SystemTextEncoding::isUtf8($filesystemElement)) {
return $filesystemElement;
}
$enc = SystemTextEncoding::getEncoding();
return SystemTextEncoding::changeCharset("UTF-8", $enc, $filesystemElement);
}
}
Expand Up @@ -1326,7 +1326,7 @@ public function switchAction($action, $httpVars, $fileVars)
$repo = ConfService::getRepositoryById($repId);
$res = 0;
if (isSet($httpVars["newLabel"])) {
$newLabel = AJXP_Utils::decodeSecureMagic($httpVars["newLabel"]);
$newLabel = AJXP_Utils::sanitize(AJXP_Utils::securePath($httpVars["newLabel"]), AJXP_SANITIZE_HTML);
if ($this->repositoryExists($newLabel)) {
AJXP_XMLWriter::header();
AJXP_XMLWriter::sendMessage(null, $mess["ajxp_conf.50"]);
Expand Down
4 changes: 2 additions & 2 deletions core/src/plugins/access.fs/class.fsAccessDriver.php
Expand Up @@ -54,7 +54,7 @@ public function initRepository()
ConfService::setConf("PROBE_REAL_SIZE", $this->getFilteredOption("PROBE_REAL_SIZE", $this->repository->getId()));
}
$create = $this->repository->getOption("CREATE");
$path = $this->repository->getOption("PATH");
$path = SystemTextEncoding::toStorageEncoding($this->repository->getOption("PATH"));
$recycle = $this->repository->getOption("RECYCLE_BIN");
$chmod = $this->repository->getOption("CHMOD_VALUE");
$wrapperData = $this->detectStreamWrapper(true);
Expand Down Expand Up @@ -2022,7 +2022,7 @@ public function makePublicletOptions($filePath, $password, $expire, $downloadlim
public function makeSharedRepositoryOptions($httpVars, $repository)
{
$newOptions = array(
"PATH" => $repository->getOption("PATH").AJXP_Utils::decodeSecureMagic($httpVars["file"]),
"PATH" => SystemTextEncoding::toStorageEncoding($repository->getOption("PATH")).AJXP_Utils::decodeSecureMagic($httpVars["file"]),
"CREATE" => isSet($httpVars["inherit_recycle"])? $repository->getOption("CREATE") : false,
"RECYCLE_BIN" => isSet($httpVars["inherit_recycle"])? $repository->getOption("RECYCLE_BIN") : "",
"DEFAULT_RIGHTS" => "");
Expand Down
6 changes: 3 additions & 3 deletions core/src/plugins/access.fs/class.fsAccessWrapper.php
Expand Up @@ -93,7 +93,7 @@ protected static function initPath($path, $streamType, $storeOpenContext = false
$tmpFileName = $tmpDir.DIRECTORY_SEPARATOR.basename($localPath);
AJXP_Logger::debug(__CLASS__,__FUNCTION__,"Tmp file $tmpFileName");
register_shutdown_function(array("fsAccessWrapper", "removeTmpFile"), $tmpDir, $tmpFileName);
$crtZip = new PclZip(AJXP_Utils::securePath(realpath($repoObject->getOption("PATH")).$repoObject->resolveVirtualRoots($zipPath)));
$crtZip = new PclZip(AJXP_Utils::securePath(realpath(SystemTextEncoding::toStorageEncoding($repoObject->getOption("PATH"))).$repoObject->resolveVirtualRoots($zipPath)));
$content = $crtZip->listContent();
foreach ($content as $item) {
$fName = AJXP_Utils::securePath($item["stored_filename"]);
Expand All @@ -116,7 +116,7 @@ protected static function initPath($path, $streamType, $storeOpenContext = false
}
}
} else {
$crtZip = new PclZip(AJXP_Utils::securePath(realpath($repoObject->getOption("PATH")).$repoObject->resolveVirtualRoots($zipPath)));
$crtZip = new PclZip(AJXP_Utils::securePath(realpath(SystemTextEncoding::toStorageEncoding($repoObject->getOption("PATH"))).$repoObject->resolveVirtualRoots($zipPath)));
$liste = $crtZip->listContent();
if($storeOpenContext) self::$crtZip = $crtZip;
$folders = array(); $files = array();$builtFolders = array();
Expand Down Expand Up @@ -178,7 +178,7 @@ protected static function initPath($path, $streamType, $storeOpenContext = false
return -1;
}
}
return realpath($repoObject->getOption("PATH")).$repoObject->resolveVirtualRoots($url["path"]);
return realpath(SystemTextEncoding::toStorageEncoding($repoObject->getOption("PATH"))).$repoObject->resolveVirtualRoots($url["path"]);
}
}

Expand Down
1 change: 1 addition & 0 deletions core/src/plugins/action.share/class.ShareCenter.php
Expand Up @@ -1797,6 +1797,7 @@ public function createSharedRepository($httpVars, $repository, $accessDriver, $u
$newRepo->setGroupPath($gPath);
}
$newRepo->setDescription($description);
$newRepo->options["PATH"] = SystemTextEncoding::fromStorageEncoding($newRepo->options["PATH"]);
if(isSet($httpVars["filter_nodes"])){
$newRepo->setContentFilter(new ContentFilter($httpVars["filter_nodes"]));
}
Expand Down
167 changes: 167 additions & 0 deletions core/src/plugins/authfront.cyphered/class.CypheredAuthFrontend.php
@@ -0,0 +1,167 @@
<?php
/*
* Copyright 2007-2013 Charles du Jeu - Abstrium SAS <team (at) pyd.io>
* 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 <http://pyd.io/>.
*/
defined('AJXP_EXEC') or die( 'Access not allowed');


class CypheredAuthFrontend extends AbstractAuthFrontend {

function detectVar(&$httpVars, $varName){
if(isSet($httpVars[$varName])) return $httpVars[$varName];
if(isSet($_SERVER["HTTP_PYDIO_".strtoupper($varName)])) return $_SERVER["HTTP_".strtoupper($varName)];
return "";
}

function getLastKeys(){
$file = $this->getPluginWorkDir(false)."/last_inc";
if(!is_file($file)) return array();
$content = file_get_contents($file);
if(empty($content)) return array();
$data = unserialize($content);
if(is_array($data)) return $data;
return array();
}

function storeLastKeys($data){
$file = $this->getPluginWorkDir(true)."/last_inc";
file_put_contents($file, serialize($data));
}

/**
* decrypt AES 256
*
* @param string $password
* @param data $edata
* @return dencrypted data
*/
public function decrypt($password, $edata) {
$data = base64_decode($edata);
$salt = substr($data, 8, 8);
$ct = substr($data, 16);
/**
* From https://github.com/mdp/gibberish-aes
*
* Number of rounds depends on the size of the AES in use
* 3 rounds for 256
* 2 rounds for the key, 1 for the IV
* 2 rounds for 128
* 1 round for the key, 1 round for the IV
* 3 rounds for 192 since it's not evenly divided by 128 bits
*/
$rounds = 3;
$data00 = $password.$salt;
$md5_hash = array();
$md5_hash[0] = md5($data00, true);
$result = $md5_hash[0];
for ($i = 1; $i < $rounds; $i++) {
$md5_hash[$i] = md5($md5_hash[$i - 1].$data00, true);
$result .= $md5_hash[$i];
}
$key = substr($result, 0, 32);
$iv = substr($result, 32,16);

return openssl_decrypt($ct, 'aes-256-cbc', $key, true, $iv);
}

/**
* crypt AES 256
*
* @param string $password
* @param string $data
* @return string encrypted data
*/
public function crypt($password, $data) {
// Set a random salt
$salt = openssl_random_pseudo_bytes(8);

$salted = '';
$dx = '';
// Salt the key(32) and iv(16) = 48
while (strlen($salted) < 48) {
$dx = md5($dx.$password.$salt, true);
$salted .= $dx;
}

$key = substr($salted, 0, 32);
$iv = substr($salted, 32,16);

$encrypted_data = openssl_encrypt($data, 'aes-256-cbc', $key, true, $iv);
return base64_encode('Salted__' . $salt . $encrypted_data);
}



function tryToLogUser(&$httpVars, $isLast = false){

$checkNonce = $this->pluginConf["CHECK_NONCE"] === true;
$token = $this->detectVar($httpVars, "cyphered_token");
$tokenInc = $this->detectVar($httpVars, "cyphered_token_inc");
if(empty($token) || ($checkNonce && empty($tokenInc))){
return false;
}

if(!$checkNonce){
$decoded = $this->decrypt($this->pluginConf["PRIVATE_KEY"], $token);
}else{
$decoded = $this->decrypt($this->pluginConf["PRIVATE_KEY"].":".$tokenInc, $token);
}
if($decoded == null){
return false;
}
$data = unserialize($decoded);
if(empty($data) || !is_array($data) || !isset($data["user_id"]) || !isset($data["user_pwd"])){
$this->logDebug(__FUNCTION__, "Cyphered Token found but wrong deserizalized data");
return false;
}
if(AuthService::getLoggedUser() != null){
$currentUser = AuthService::getLoggedUser()->getId();
if($currentUser != $data["user_id"]){
AuthService::disconnect();
}
}
$this->logDebug(__FUNCTION__, "Trying to log user ".$data["user_id"]." from cyphered token");
$userId = $data["user_id"];
if($checkNonce){
$keys = $this->getLastKeys();
$lastInc = 0;
if(isSet($keys[$userId])){
$lastInc = $keys[$userId];
}
if($tokenInc <= $lastInc){
$this->logDebug(__FUNCTION__, "Key was already used for this user id");
return false;
}
}
$res = AuthService::logUser($data["user_id"], $data["user_pwd"], false, false, -1);
if($res > 0) {
$this->logDebug(__FUNCTION__, "Success");
if($checkNonce){
$keys[$userId] = $tokenInc;
$this->storeLastKeys($keys);
}
return true;
}

$this->logDebug(__FUNCTION__, "Wrong result ".$res);
return false;

}

}
16 changes: 16 additions & 0 deletions core/src/plugins/authfront.cyphered/manifest.xml
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<ajxpcore id="authfront.cyphered" label="CONF_MESSAGE[Cyphered Identifiers]" enabled="false" description="CONF_MESSAGE[Cyphered identifiers passed through GET, POST or HTTP Headers, using a private key shared by Pydio and Third-party application.]"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="file:../core.ajaxplorer/ajxp_registry.xsd">
<class_definition filename="plugins/authfront.cyphered/class.CypheredAuthFrontend.php" classname="CypheredAuthFrontend"/>
<server_settings>
<global_param name="PRIVATE_KEY" type="string" label="Private Key"
description="Key used for hashing" mandatory="true"/>
<global_param name="CHECK_NONCE" type="boolean" label="Replay Check"
description="Pass a unique, incrementing, integer on each query to make it impossible to replay twice. Better security." mandatory="true" default="true"/>
<global_param name="ORDER" type="integer" label="Order"
description="Order this plugin with other auth frontends" default="2"/>
<global_param name="PROTOCOL_TYPE" type="select"
choices="session_only|Sessions Only,no_session|REST Only,both|Session-based or Rest"
label="Protocol Type" description="Enable/disable automatically based on the protocol used" default="both"/>
</server_settings>
</ajxpcore>
47 changes: 47 additions & 0 deletions core/src/plugins/authfront.cyphered/plugin_doc.html
@@ -0,0 +1,47 @@
This is plugin aims at passing credentials directly through a POST to Pydio.<br>
User ID and PWD are expected to be passed in an encrypted token using the standard Open SSL functions (openssl extension must be enabled).
A simple incremental "nonce" is used to make sure the token can never be replayed.<bR>
<br>
Here is the sample PHP code to use to cypher the password:
<pre>
/**
* crypt AES 256
*
* @param string $password
* @param string $data
* @return string Base64 encoded encrypted data.
*/
function PYDIO_crypt($password, $data) {
// Set a random salt
$salt = openssl_random_pseudo_bytes(8);

$salted = '';
$dx = '';
// Salt the key(32) and iv(16) = 48
while (strlen($salted) < 48) {
$dx = md5($dx.$password.$salt, true);
$salted .= $dx;
}

$key = substr($salted, 0, 32);
$iv = substr($salted, 32,16);

$encrypted_data = openssl_encrypt($data, 'aes-256-cbc', $key, true, $iv);
return base64_encode('Salted__' . $salt . $encrypted_data);
}


$testUser = "USER_ID";
$testPwd = "USER_PASSWORD";
$privateKey = "YOUR_PRIVATE_KEY_AS_CONFIGURED_IN_PLUGIN";
// IF REPLAY CHECK OPTION IS ENABLED
$tokenInc = 1; // IMPORTANT: INCREMENT THIS AT EACH CALL

$serial = serialize(array("user_id" => $testUser, "user_pwd" => $testPwd));
$token = urlencode(PYDIO_crypt(isSet($tokenInc)?$privateKey.":".$tokenInc:$privateKey, $serial));

// BUILD AN URL WITH HIDDEN POSTS, OR GET:
// SKIP cyphered_token_inc if REPLAY_CHECK is not enabled.
$URL = "https://yourserver/path/?cyphered_token=$token&cyphered_token_inc=$tokenInc";

</pre>
2 changes: 1 addition & 1 deletion core/src/plugins/meta.mount/class.FilesystemMounter.php
Expand Up @@ -150,7 +150,7 @@ public function mountFS()
$output = shell_exec($cmd1);
$success = !empty($output);
}else{
$success = ($res == 0);
$success = ($res == 0 || $res == 32);
}
if (!$success) {
throw new Exception("Error while mounting file system!");
Expand Down

0 comments on commit b6f9e98

Please sign in to comment.