summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--apps/files_external/ajax/addRootCertificate.php45
-rw-r--r--apps/files_external/ajax/removeRootCertificate.php13
-rwxr-xr-xapps/files_external/lib/config.php47
-rwxr-xr-xapps/files_external/personal.php1
-rw-r--r--apps/files_external/templates/settings.php27
-rw-r--r--apps/files_sharing/lib/external/manager.php4
-rw-r--r--apps/files_sharing/lib/external/storage.php10
-rw-r--r--apps/files_sharing/tests/externalstorage.php4
-rw-r--r--lib/private/l10n.php2
-rw-r--r--lib/private/security/certificate.php126
-rw-r--r--lib/private/security/certificatemanager.php134
-rw-r--r--lib/private/server.php18
-rw-r--r--lib/public/icertificate.php56
-rw-r--r--lib/public/icertificatemanager.php40
-rw-r--r--lib/public/iservercontainer.php7
-rw-r--r--settings/ajax/addRootCertificate.php32
-rw-r--r--settings/ajax/removeRootCertificate.php7
-rw-r--r--settings/css/settings.css8
-rw-r--r--settings/js/personal.js214
-rw-r--r--settings/personal.php2
-rw-r--r--settings/routes.php4
-rw-r--r--settings/templates/personal.php46
-rw-r--r--tests/data/certificates/badCertificate.crt13
-rw-r--r--tests/data/certificates/expiredCertificate.crt13
-rw-r--r--tests/data/certificates/goodCertificate.crt15
-rw-r--r--tests/lib/security/certificate.php93
-rw-r--r--tests/lib/security/certificatemanager.php87
27 files changed, 838 insertions, 230 deletions
diff --git a/apps/files_external/ajax/addRootCertificate.php b/apps/files_external/ajax/addRootCertificate.php
deleted file mode 100644
index fcd3a617ada..00000000000
--- a/apps/files_external/ajax/addRootCertificate.php
+++ /dev/null
@@ -1,45 +0,0 @@
-<?php
-
-OCP\JSON::checkAppEnabled('files_external');
-OCP\JSON::callCheck();
-
-if ( ! ($filename = $_FILES['rootcert_import']['name']) ) {
- header('Location:' . OCP\Util::linkToRoute( "settings_personal" ));
- exit;
-}
-
-$fh = fopen($_FILES['rootcert_import']['tmp_name'], 'r');
-$data = fread($fh, filesize($_FILES['rootcert_import']['tmp_name']));
-fclose($fh);
-$filename = $_FILES['rootcert_import']['name'];
-
-$view = new \OC\Files\View('/'.\OCP\User::getUser().'/files_external/uploads');
-if (!$view->file_exists('')) {
- $view->mkdir('');
-}
-
-$isValid = openssl_pkey_get_public($data);
-
-//maybe it was just the wrong file format, try to convert it...
-if ($isValid == false) {
- $data = chunk_split(base64_encode($data), 64, "\n");
- $data = "-----BEGIN CERTIFICATE-----\n".$data."-----END CERTIFICATE-----\n";
- $isValid = openssl_pkey_get_public($data);
-}
-
-// add the certificate if it could be verified
-if ( $isValid ) {
- // disable proxy to prevent multiple fopen calls
- $proxyStatus = \OC_FileProxy::$enabled;
- \OC_FileProxy::$enabled = false;
- $view->file_put_contents($filename, $data);
- OC_Mount_Config::createCertificateBundle();
- \OC_FileProxy::$enabled = $proxyStatus;
-} else {
- OCP\Util::writeLog('files_external',
- 'Couldn\'t import SSL root certificate ('.$filename.'), allowed formats: PEM and DER',
- OCP\Util::WARN);
-}
-
-header('Location:' . OCP\Util::linkToRoute( "settings_personal" ));
-exit;
diff --git a/apps/files_external/ajax/removeRootCertificate.php b/apps/files_external/ajax/removeRootCertificate.php
deleted file mode 100644
index 664b3937e97..00000000000
--- a/apps/files_external/ajax/removeRootCertificate.php
+++ /dev/null
@@ -1,13 +0,0 @@
-<?php
-
-OCP\JSON::checkAppEnabled('files_external');
-OCP\JSON::checkLoggedIn();
-OCP\JSON::callCheck();
-
-$view = \OCP\Files::getStorage("files_external");
-$file = 'uploads/'.ltrim($_POST['cert'], "/\\.");
-
-if ( $view->file_exists($file) ) {
- $view->unlink($file);
- OC_Mount_Config::createCertificateBundle();
-}
diff --git a/apps/files_external/lib/config.php b/apps/files_external/lib/config.php
index 85e36fd9043..952463b8015 100755
--- a/apps/files_external/lib/config.php
+++ b/apps/files_external/lib/config.php
@@ -620,53 +620,6 @@ class OC_Mount_Config {
}
/**
- * Returns all user uploaded ssl root certificates
- * @return array
- */
- public static function getCertificates() {
- $path=OC_User::getHome(OC_User::getUser()) . '/files_external/uploads/';
- \OCP\Util::writeLog('files_external', 'checking path '.$path, \OCP\Util::INFO);
- if ( ! is_dir($path)) {
- //path might not exist (e.g. non-standard OC_User::getHome() value)
- //in this case create full path using 3rd (recursive=true) parameter.
- mkdir($path, 0777, true);
- }
- $result = array();
- $handle = opendir($path);
- if(!is_resource($handle)) {
- return array();
- }
- while (false !== ($file = readdir($handle))) {
- if ($file != '.' && $file != '..') $result[] = $file;
- }
- return $result;
- }
-
- /**
- * creates certificate bundle
- */
- public static function createCertificateBundle() {
- $path=OC_User::getHome(OC_User::getUser()) . '/files_external';
-
- $certs = OC_Mount_Config::getCertificates();
- $fh_certs = fopen($path."/rootcerts.crt", 'w');
- foreach ($certs as $cert) {
- $file=$path.'/uploads/'.$cert;
- $fh = fopen($file, "r");
- $data = fread($fh, filesize($file));
- fclose($fh);
- if (strpos($data, 'BEGIN CERTIFICATE')) {
- fwrite($fh_certs, $data);
- fwrite($fh_certs, "\r\n");
- }
- }
-
- fclose($fh_certs);
-
- return true;
- }
-
- /**
* check dependencies
*/
public static function checkDependencies() {
diff --git a/apps/files_external/personal.php b/apps/files_external/personal.php
index 90d7afed28b..a279163ff70 100755
--- a/apps/files_external/personal.php
+++ b/apps/files_external/personal.php
@@ -27,7 +27,6 @@ $backends = OC_Mount_Config::getPersonalBackends();
$tmpl = new OCP\Template('files_external', 'settings');
$tmpl->assign('isAdminPage', false);
$tmpl->assign('mounts', OC_Mount_Config::getPersonalMountPoints());
-$tmpl->assign('certs', OC_Mount_Config::getCertificates());
$tmpl->assign('dependencies', OC_Mount_Config::checkDependencies());
$tmpl->assign('backends', $backends);
return $tmpl->fetchPage();
diff --git a/apps/files_external/templates/settings.php b/apps/files_external/templates/settings.php
index dd283f9ff55..072f856dfbd 100644
--- a/apps/files_external/templates/settings.php
+++ b/apps/files_external/templates/settings.php
@@ -119,30 +119,3 @@
</p>
<?php endif; ?>
</form>
-
-<?php if ( ! $_['isAdminPage']): ?>
-<form id="files_external" class="section"
- method="post"
- enctype="multipart/form-data"
- action="<?php p(OCP\Util::linkTo('files_external', 'ajax/addRootCertificate.php')); ?>">
- <h2><?php p($l->t('SSL root certificates'));?></h2>
- <table id="sslCertificate" data-admin='<?php print_unescaped(json_encode($_['isAdminPage'])); ?>'>
- <tbody>
- <?php foreach ($_['certs'] as $rootCert): ?>
- <tr id="<?php p($rootCert) ?>">
- <td class="rootCert"><?php p($rootCert) ?></td>
- <td <?php if ($rootCert != ''): ?>class="remove"
- <?php else: ?>style="visibility:hidden;"
- <?php endif; ?>><img alt="<?php p($l->t('Delete')); ?>"
- title="<?php p($l->t('Delete')); ?>"
- class="svg action"
- src="<?php print_unescaped(image_path('core', 'actions/delete.svg')); ?>" /></td>
- </tr>
- <?php endforeach; ?>
- </tbody>
- </table>
- <input type="hidden" name="requesttoken" value="<?php p($_['requesttoken']); ?>">
- <input type="file" id="rootcert_import" name="rootcert_import">
- <input type="submit" name="cert_import" value="<?php p($l->t('Import Root Certificate')); ?>" />
-</form>
-<?php endif; ?>
diff --git a/apps/files_sharing/lib/external/manager.php b/apps/files_sharing/lib/external/manager.php
index dda283f4952..8176302a86a 100644
--- a/apps/files_sharing/lib/external/manager.php
+++ b/apps/files_sharing/lib/external/manager.php
@@ -113,9 +113,11 @@ class Manager {
* @return Mount
*/
protected function mountShare($data) {
+ $user = $this->userSession->getUser();
$data['manager'] = $this;
- $mountPoint = '/' . $this->userSession->getUser()->getUID() . '/files' . $data['mountpoint'];
+ $mountPoint = '/' . $user->getUID() . '/files' . $data['mountpoint'];
$data['mountpoint'] = $mountPoint;
+ $data['certificateManager'] = \OC::$server->getCertificateManager($user);
$mount = new Mount(self::STORAGE, $mountPoint, $data, $this, $this->storageLoader);
$this->mountManager->addMount($mount);
return $mount;
diff --git a/apps/files_sharing/lib/external/storage.php b/apps/files_sharing/lib/external/storage.php
index 855be2872b5..92d8f92b380 100644
--- a/apps/files_sharing/lib/external/storage.php
+++ b/apps/files_sharing/lib/external/storage.php
@@ -37,6 +37,11 @@ class Storage extends DAV implements ISharedStorage {
*/
private $token;
+ /**
+ * @var \OCP\ICertificateManager
+ */
+ private $certificateManager;
+
private $updateChecked = false;
/**
@@ -46,6 +51,7 @@ class Storage extends DAV implements ISharedStorage {
public function __construct($options) {
$this->manager = $options['manager'];
+ $this->certificateManager = $options['certificateManager'];
$this->remote = $options['remote'];
$this->remoteUser = $options['owner'];
list($protocol, $remote) = explode('://', $this->remote);
@@ -190,6 +196,10 @@ class Storage extends DAV implements ISharedStorage {
http_build_query(array('password' => $password)));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+ curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
+ curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
+ curl_setopt($ch, CURLOPT_CAINFO, $this->certificateManager->getCertificateBundle());
+
$result = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
diff --git a/apps/files_sharing/tests/externalstorage.php b/apps/files_sharing/tests/externalstorage.php
index 1258148af53..2e93afa1987 100644
--- a/apps/files_sharing/tests/externalstorage.php
+++ b/apps/files_sharing/tests/externalstorage.php
@@ -65,6 +65,7 @@ class Test_Files_Sharing_External_Storage extends \PHPUnit_Framework_TestCase {
* @dataProvider optionsProvider
*/
public function testStorageMountOptions($inputUri, $baseUri) {
+ $certificateManager = \OC::$server->getCertificateManager();
$storage = new TestSharingExternalStorage(
array(
'remote' => $inputUri,
@@ -72,7 +73,8 @@ class Test_Files_Sharing_External_Storage extends \PHPUnit_Framework_TestCase {
'mountpoint' => 'remoteshare',
'token' => 'abcdef',
'password' => '',
- 'manager' => null
+ 'manager' => null,
+ 'certificateManager' => $certificateManager
)
);
$this->assertEquals($baseUri, $storage->getBaseUri());
diff --git a/lib/private/l10n.php b/lib/private/l10n.php
index 28b35e92a2f..57886a796cd 100644
--- a/lib/private/l10n.php
+++ b/lib/private/l10n.php
@@ -354,7 +354,7 @@ class OC_L10N implements \OCP\IL10N {
case 'datetime':
case 'time':
if($data instanceof DateTime) {
- return $data->format($this->localizations[$type]);
+ $data = $data->getTimestamp();
} elseif(is_string($data) && !is_numeric($data)) {
$data = strtotime($data);
}
diff --git a/lib/private/security/certificate.php b/lib/private/security/certificate.php
new file mode 100644
index 00000000000..778524507e0
--- /dev/null
+++ b/lib/private/security/certificate.php
@@ -0,0 +1,126 @@
+<?php
+/**
+ * Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+namespace OC\Security;
+
+use OCP\ICertificate;
+
+class Certificate implements ICertificate {
+ protected $name;
+
+ protected $commonName;
+
+ protected $organization;
+
+ protected $serial;
+
+ protected $issueDate;
+
+ protected $expireDate;
+
+ protected $issuerName;
+
+ protected $issuerOrganization;
+
+ /**
+ * @param string $data base64 encoded certificate
+ * @param string $name
+ * @throws \Exception If the certificate could not get parsed
+ */
+ public function __construct($data, $name) {
+ $this->name = $name;
+ try {
+ $gmt = new \DateTimeZone('GMT');
+ $info = openssl_x509_parse($data);
+ $this->commonName = isset($info['subject']['CN']) ? $info['subject']['CN'] : null;
+ $this->organization = isset($info['subject']['O']) ? $info['subject']['O'] : null;
+ $this->serial = $this->formatSerial($info['serialNumber']);
+ $this->issueDate = new \DateTime('@' . $info['validFrom_time_t'], $gmt);
+ $this->expireDate = new \DateTime('@' . $info['validTo_time_t'], $gmt);
+ $this->issuerName = isset($info['issuer']['CN']) ? $info['issuer']['CN'] : null;
+ $this->issuerOrganization = isset($info['issuer']['O']) ? $info['issuer']['O'] : null;
+ } catch (\Exception $e) {
+ throw new \Exception('Certificate could not get parsed.');
+ }
+ }
+
+ /**
+ * Format the numeric serial into AA:BB:CC hex format
+ *
+ * @param int $serial
+ * @return string
+ */
+ protected function formatSerial($serial) {
+ $hex = strtoupper(dechex($serial));
+ return trim(chunk_split($hex, 2, ':'), ':');
+ }
+
+ /**
+ * @return string
+ */
+ public function getName() {
+ return $this->name;
+ }
+
+ /**
+ * @return string|null
+ */
+ public function getCommonName() {
+ return $this->commonName;
+ }
+
+ /**
+ * @return string
+ */
+ public function getOrganization() {
+ return $this->organization;
+ }
+
+ /**
+ * @return string
+ */
+ public function getSerial() {
+ return $this->serial;
+ }
+
+ /**
+ * @return \DateTime
+ */
+ public function getIssueDate() {
+ return $this->issueDate;
+ }
+
+ /**
+ * @return \DateTime
+ */
+ public function getExpireDate() {
+ return $this->expireDate;
+ }
+
+ /**
+ * @return bool
+ */
+ public function isExpired() {
+ $now = new \DateTime();
+ return $this->issueDate > $now or $now > $this->expireDate;
+ }
+
+ /**
+ * @return string|null
+ */
+ public function getIssuerName() {
+ return $this->issuerName;
+ }
+
+ /**
+ * @return string|null
+ */
+ public function getIssuerOrganization() {
+ return $this->issuerOrganization;
+ }
+}
diff --git a/lib/private/security/certificatemanager.php b/lib/private/security/certificatemanager.php
new file mode 100644
index 00000000000..cae9730eb26
--- /dev/null
+++ b/lib/private/security/certificatemanager.php
@@ -0,0 +1,134 @@
+<?php
+/**
+ * Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+namespace OC\Security;
+
+use OC\Files\Filesystem;
+use OCP\ICertificateManager;
+
+/**
+ * Manage trusted certificates for users
+ */
+class CertificateManager implements ICertificateManager {
+ /**
+ * @var \OCP\IUser
+ */
+ protected $user;
+
+ /**
+ * @param \OCP\IUser $user
+ */
+ public function __construct($user) {
+ $this->user = $user;
+ }
+
+ /**
+ * Returns all certificates trusted by the user
+ *
+ * @return \OCP\ICertificate[]
+ */
+ public function listCertificates() {
+ $path = $this->user->getHome() . '/files_external/uploads/';
+ if (!is_dir($path)) {
+ return array();
+ }
+ $result = array();
+ $handle = opendir($path);
+ if (!is_resource($handle)) {
+ return array();
+ }
+ while (false !== ($file = readdir($handle))) {
+ if ($file != '.' && $file != '..') {
+ try {
+ $result[] = new Certificate(file_get_contents($path . $file), $file);
+ } catch(\Exception $e) {}
+ }
+ }
+ return $result;
+ }
+
+ /**
+ * create the certificate bundle of all trusted certificated
+ */
+ protected function createCertificateBundle() {
+ $path = $this->user->getHome() . '/files_external/';
+ $certs = $this->listCertificates();
+
+ $fh_certs = fopen($path . '/rootcerts.crt', 'w');
+ foreach ($certs as $cert) {
+ $file = $path . '/uploads/' . $cert->getName();
+ $data = file_get_contents($file);
+ if (strpos($data, 'BEGIN CERTIFICATE')) {
+ fwrite($fh_certs, $data);
+ fwrite($fh_certs, "\r\n");
+ }
+ }
+
+ fclose($fh_certs);
+ }
+
+ /**
+ * Save the certificate and re-generate the certificate bundle
+ *
+ * @param string $certificate the certificate data
+ * @param string $name the filename for the certificate
+ * @return \OCP\ICertificate|void|bool
+ * @throws \Exception If the certificate could not get added
+ */
+ public function addCertificate($certificate, $name) {
+ if (!Filesystem::isValidPath($name) or Filesystem::isFileBlacklisted($name)) {
+ return false;
+ }
+
+ $dir = $this->user->getHome() . '/files_external/uploads/';
+ if (!file_exists($dir)) {
+ //path might not exist (e.g. non-standard OC_User::getHome() value)
+ //in this case create full path using 3rd (recursive=true) parameter.
+ //note that we use "normal" php filesystem functions here since the certs need to be local
+ mkdir($dir, 0700, true);
+ }
+
+ try {
+ $file = $dir . $name;
+ $certificateObject = new Certificate($certificate, $name);
+ file_put_contents($file, $certificate);
+ $this->createCertificateBundle();
+ return $certificateObject;
+ } catch (\Exception $e) {
+ throw $e;
+ }
+
+ }
+
+ /**
+ * Remove the certificate and re-generate the certificate bundle
+ *
+ * @param string $name
+ * @return bool
+ */
+ public function removeCertificate($name) {
+ if (!Filesystem::isValidPath($name)) {
+ return false;
+ }
+ $path = $this->user->getHome() . '/files_external/uploads/';
+ if (file_exists($path . $name)) {
+ unlink($path . $name);
+ $this->createCertificateBundle();
+ }
+ return true;
+ }
+
+ /**
+ * Get the path to the certificate bundle for this user
+ *
+ * @return string
+ */
+ public function getCertificateBundle() {
+ return $this->user->getHome() . '/files_external/rootcerts.crt';
+ }
+}
diff --git a/lib/private/server.php b/lib/private/server.php
index 28c4fe60856..5d40f1327f6 100644
--- a/lib/private/server.php
+++ b/lib/private/server.php
@@ -6,6 +6,7 @@ use OC\AppFramework\Http\Request;
use OC\AppFramework\Db\Db;
use OC\AppFramework\Utility\SimpleContainer;
use OC\Cache\UserCache;
+use OC\Security\CertificateManager;
use OC\DB\ConnectionWrapper;
use OC\Files\Node\Root;
use OC\Files\View;
@@ -474,4 +475,21 @@ class Server extends SimpleContainer implements IServerContainer {
function getDb() {
return $this->query('Db');
}
+
+ /**
+ * Get the certificate manager for the user
+ *
+ * @param \OCP\IUser $user (optional) if not specified the current loggedin user is used
+ * @return \OCP\ICertificateManager
+ */
+ function getCertificateManager($user = null) {
+ if (is_null($user)) {
+ $userSession = $this->getUserSession();
+ $user = $userSession->getUser();
+ if (is_null($user)) {
+ return null;
+ }
+ }
+ return new CertificateManager($user);
+ }
}
diff --git a/lib/public/icertificate.php b/lib/public/icertificate.php
new file mode 100644
index 00000000000..013496cb373
--- /dev/null
+++ b/lib/public/icertificate.php
@@ -0,0 +1,56 @@
+<?php
+/**
+ * Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+namespace OCP;
+
+interface ICertificate {
+ /**
+ * @return string
+ */
+ public function getName();
+
+ /**
+ * @return string
+ */
+ public function getCommonName();
+
+ /**
+ * @return string
+ */
+ public function getOrganization();
+
+ /**
+ * @return string
+ */
+ public function getSerial();
+
+ /**
+ * @return \DateTime
+ */
+ public function getIssueDate();
+
+ /**
+ * @return \DateTime
+ */
+ public function getExpireDate();
+
+ /**
+ * @return bool
+ */
+ public function isExpired();
+
+ /**
+ * @return string
+ */
+ public function getIssuerName();
+
+ /**
+ * @return string
+ */
+ public function getIssuerOrganization();
+}
diff --git a/lib/public/icertificatemanager.php b/lib/public/icertificatemanager.php
new file mode 100644
index 00000000000..24b8d123634
--- /dev/null
+++ b/lib/public/icertificatemanager.php
@@ -0,0 +1,40 @@
+<?php
+/**
+ * Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+namespace OCP;
+
+/**
+ * Manage trusted certificates for users
+ */
+interface ICertificateManager {
+ /**
+ * Returns all certificates trusted by the user
+ *
+ * @return \OCP\ICertificate[]
+ */
+ public function listCertificates();
+
+ /**
+ * @param string $certificate the certificate data
+ * @param string $name the filename for the certificate
+ * @return bool | \OCP\ICertificate
+ */
+ public function addCertificate($certificate, $name);
+
+ /**
+ * @param string $name
+ */
+ public function removeCertificate($name);
+
+ /**
+ * Get the path to the certificate bundle for this user
+ *
+ * @return string
+ */
+ public function getCertificateBundle();
+}
diff --git a/lib/public/iservercontainer.php b/lib/public/iservercontainer.php
index 64f5f350b1e..60b0b497c54 100644
--- a/lib/public/iservercontainer.php
+++ b/lib/public/iservercontainer.php
@@ -228,4 +228,11 @@ interface IServerContainer {
*/
function getSearch();
+ /**
+ * Get the certificate manager for the user
+ *
+ * @param \OCP\IUser $user (optional) if not specified the current loggedin user is used
+ * @return \OCP\ICertificateManager
+ */
+ function getCertificateManager($user = null);
}
diff --git a/settings/ajax/addRootCertificate.php b/settings/ajax/addRootCertificate.php
new file mode 100644
index 00000000000..378ef39c1e5
--- /dev/null
+++ b/settings/ajax/addRootCertificate.php
@@ -0,0 +1,32 @@
+<?php
+OCP\JSON::checkLoggedIn();
+OCP\JSON::callCheck();
+
+$l = new OC_L10N('core');
+
+if (!isset($_FILES['rootcert_import'])) {
+ OCP\JSON::error(array('error' => 'No certificate uploaded'));
+ exit;
+}
+
+$data = file_get_contents($_FILES['rootcert_import']['tmp_name']);
+$filename = basename($_FILES['rootcert_import']['name']);
+
+$certificateManager = \OC::$server->getCertificateManager();
+
+try {
+ $cert = $certificateManager->addCertificate($data, $filename);
+ OCP\JSON::success(array(
+ 'name' => $cert->getName(),
+ 'commonName' => $cert->getCommonName(),
+ 'organization' => $cert->getOrganization(),
+ 'validFrom' => $cert->getIssueDate()->getTimestamp(),
+ 'validTill' => $cert->getExpireDate()->getTimestamp(),
+ 'validFromString' => $l->l('date', $cert->getIssueDate()),
+ 'validTillString' => $l->l('date', $cert->getExpireDate()),
+ 'issuer' => $cert->getIssuerName(),
+ 'issuerOrganization' => $cert->getIssuerOrganization()
+ ));
+} catch(\Exception $e) {
+ OCP\JSON::error(array('error' => 'Couldn\'t import SSL root certificate, allowed formats: PEM and DER'));
+}
diff --git a/settings/ajax/removeRootCertificate.php b/settings/ajax/removeRootCertificate.php
new file mode 100644
index 00000000000..a3de035269e
--- /dev/null
+++ b/settings/ajax/removeRootCertificate.php
@@ -0,0 +1,7 @@
+<?php
+OCP\JSON::checkLoggedIn();
+OCP\JSON::callCheck();
+
+$name = $_POST['cert'];
+$certificateManager = \OC::$server->getCertificateManager();
+$certificateManager->removeCertificate($name);
diff --git a/settings/css/settings.css b/settings/css/settings.css
index a62a971b831..95fab85df97 100644
--- a/settings/css/settings.css
+++ b/settings/css/settings.css
@@ -6,9 +6,11 @@ select#languageinput, select#timezone { width:15em; }
input#openid, input#webdav { width:20em; }
/* PERSONAL */
-#rootcert_import {
- margin: 0 0 10px 0;
- display: block;
+#sslCertificate tr.expired {
+ background-color: rgba(255, 0, 0, 0.5);
+}
+#sslCertificate td {
+ padding: 5px;
}
/* Sync clients */
diff --git a/settings/js/personal.js b/settings/js/personal.js
index f56dd3425f7..11e9593d74a 100644
--- a/settings/js/personal.js
+++ b/settings/js/personal.js
@@ -13,12 +13,12 @@
*
* @param callback
*/
-jQuery.fn.keyUpDelayedOrEnter = function(callback){
+jQuery.fn.keyUpDelayedOrEnter = function (callback) {
var cb = callback;
var that = this;
this.keyup(_.debounce(function (event) {
// enter is already handled in keypress
- if(event.keyCode === 13) {
+ if (event.keyCode === 13) {
return;
}
if (that.val() !== '') {
@@ -27,7 +27,7 @@ jQuery.fn.keyUpDelayedOrEnter = function(callback){
}, 1000));
this.keypress(function (event) {
- if (event.keyCode === 13 && that.val() !== '' ){
+ if (event.keyCode === 13 && that.val() !== '') {
event.preventDefault();
cb();
}
@@ -38,48 +38,48 @@ jQuery.fn.keyUpDelayedOrEnter = function(callback){
/**
* Post the email address change to the server.
*/
-function changeEmailAddress(){
- var emailInfo = $('#email');
- if (emailInfo.val() === emailInfo.defaultValue){
- return;
- }
- emailInfo.defaultValue = emailInfo.val();
- OC.msg.startSaving('#lostpassword .msg');
- var post = $( "#lostpassword" ).serialize();
- $.post( 'ajax/lostpassword.php', post, function(data){
- OC.msg.finishedSaving('#lostpassword .msg', data);
- });
+function changeEmailAddress () {
+ var emailInfo = $('#email');
+ if (emailInfo.val() === emailInfo.defaultValue) {
+ return;
+ }
+ emailInfo.defaultValue = emailInfo.val();
+ OC.msg.startSaving('#lostpassword .msg');
+ var post = $("#lostpassword").serialize();
+ $.post('ajax/lostpassword.php', post, function (data) {
+ OC.msg.finishedSaving('#lostpassword .msg', data);
+ });
}
/**
* Post the display name change to the server.
*/
-function changeDisplayName(){
- if ($('#displayName').val() !== '' ) {
- OC.msg.startSaving('#displaynameform .msg');
- // Serialize the data
- var post = $( "#displaynameform" ).serialize();
- // Ajax foo
- $.post( 'ajax/changedisplayname.php', post, function(data){
- if( data.status === "success" ){
- $('#oldDisplayName').val($('#displayName').val());
- // update displayName on the top right expand button
- $('#expandDisplayName').text($('#displayName').val());
- updateAvatar();
- }
- else{
- $('#newdisplayname').val(data.data.displayName);
- }
- OC.msg.finishedSaving('#displaynameform .msg', data);
- });
- }
+function changeDisplayName () {
+ if ($('#displayName').val() !== '') {
+ OC.msg.startSaving('#displaynameform .msg');
+ // Serialize the data
+ var post = $("#displaynameform").serialize();
+ // Ajax foo
+ $.post('ajax/changedisplayname.php', post, function (data) {
+ if (data.status === "success") {
+ $('#oldDisplayName').val($('#displayName').val());
+ // update displayName on the top right expand button
+ $('#expandDisplayName').text($('#displayName').val());
+ updateAvatar();
+ }
+ else {
+ $('#newdisplayname').val(data.data.displayName);
+ }
+ OC.msg.finishedSaving('#displaynameform .msg', data);
+ });
+ }
}
function updateAvatar (hidedefault) {
var $headerdiv = $('#header .avatardiv');
var $displaydiv = $('#displayavatar .avatardiv');
- if(hidedefault) {
+ if (hidedefault) {
$headerdiv.hide();
$('#header .avatardiv').removeClass('avatardiv-shown');
} else {
@@ -93,16 +93,16 @@ function updateAvatar (hidedefault) {
$('#removeavatar').show();
}
-function showAvatarCropper() {
+function showAvatarCropper () {
var $cropper = $('#cropper');
$cropper.prepend("<img>");
var $cropperImage = $('#cropper img');
$cropperImage.attr('src',
- OC.generateUrl('/avatar/tmp')+'?requesttoken='+oc_requesttoken+'#'+Math.floor(Math.random()*1000));
+ OC.generateUrl('/avatar/tmp') + '?requesttoken=' + oc_requesttoken + '#' + Math.floor(Math.random() * 1000));
// Looks weird, but on('load', ...) doesn't work in IE8
- $cropperImage.ready(function(){
+ $cropperImage.ready(function () {
$('#displayavatar').hide();
$cropper.show();
@@ -117,7 +117,7 @@ function showAvatarCropper() {
});
}
-function sendCropData() {
+function sendCropData () {
cleanCropper();
var cropperData = $('#cropper').data();
@@ -130,11 +130,11 @@ function sendCropData() {
$.post(OC.generateUrl('/avatar/cropped'), {crop: data}, avatarResponseHandler);
}
-function saveCoords(c) {
+function saveCoords (c) {
$('#cropper').data(c);
}
-function cleanCropper() {
+function cleanCropper () {
var $cropper = $('#cropper');
$('#displayavatar').show();
$cropper.hide();
@@ -143,7 +143,7 @@ function cleanCropper() {
$('#cropper img').remove();
}
-function avatarResponseHandler(data) {
+function avatarResponseHandler (data) {
var $warning = $('#avatar .warning');
$warning.hide();
if (data.status === "success") {
@@ -156,20 +156,20 @@ function avatarResponseHandler(data) {
}
}
-$(document).ready(function(){
- $("#passwordbutton").click( function(){
+$(document).ready(function () {
+ $("#passwordbutton").click(function () {
if ($('#pass1').val() !== '' && $('#pass2').val() !== '') {
// Serialize the data
- var post = $( "#passwordform" ).serialize();
+ var post = $("#passwordform").serialize();
$('#passwordchanged').hide();
$('#passworderror').hide();
// Ajax foo
- $.post(OC.generateUrl('/settings/personal/changepassword'), post, function(data){
- if( data.status === "success" ){
+ $.post(OC.generateUrl('/settings/personal/changepassword'), post, function (data) {
+ if (data.status === "success") {
$('#pass1').val('');
$('#pass2').val('');
$('#passwordchanged').show();
- } else{
+ } else {
if (typeof(data.data) !== "undefined") {
$('#passworderror').html(data.data.message);
} else {
@@ -190,22 +190,22 @@ $(document).ready(function(){
$('#displayName').keyUpDelayedOrEnter(changeDisplayName);
$('#email').keyUpDelayedOrEnter(changeEmailAddress);
- $("#languageinput").change( function(){
+ $("#languageinput").change(function () {
// Serialize the data
- var post = $( "#languageinput" ).serialize();
+ var post = $("#languageinput").serialize();
// Ajax foo
- $.post( 'ajax/setlanguage.php', post, function(data){
- if( data.status === "success" ){
+ $.post('ajax/setlanguage.php', post, function (data) {
+ if (data.status === "success") {
location.reload();
}
- else{
- $('#passworderror').html( data.data.message );
+ else {
+ $('#passworderror').html(data.data.message);
}
});
return false;
});
- $('button:button[name="submitDecryptAll"]').click(function() {
+ $('button:button[name="submitDecryptAll"]').click(function () {
var privateKeyPassword = $('#decryptAll input:password[id="privateKeyPassword"]').val();
$('#decryptAll button:button[name="submitDecryptAll"]').prop("disabled", true);
$('#decryptAll input:password[name="privateKeyPassword"]').prop("disabled", true);
@@ -213,23 +213,23 @@ $(document).ready(function(){
});
- $('button:button[name="submitRestoreKeys"]').click(function() {
+ $('button:button[name="submitRestoreKeys"]').click(function () {
$('#restoreBackupKeys button:button[name="submitDeleteKeys"]').prop("disabled", true);
$('#restoreBackupKeys button:button[name="submitRestoreKeys"]').prop("disabled", true);
OC.Encryption.restoreKeys();
});
- $('button:button[name="submitDeleteKeys"]').click(function() {
+ $('button:button[name="submitDeleteKeys"]').click(function () {
$('#restoreBackupKeys button:button[name="submitDeleteKeys"]').prop("disabled", true);
$('#restoreBackupKeys button:button[name="submitRestoreKeys"]').prop("disabled", true);
OC.Encryption.deleteKeys();
});
- $('#decryptAll input:password[name="privateKeyPassword"]').keyup(function(event) {
+ $('#decryptAll input:password[name="privateKeyPassword"]').keyup(function (event) {
var privateKeyPassword = $('#decryptAll input:password[id="privateKeyPassword"]').val();
- if (privateKeyPassword !== '' ) {
+ if (privateKeyPassword !== '') {
$('#decryptAll button:button[name="submitDecryptAll"]').prop("disabled", false);
- if(event.which === 13) {
+ if (event.which === 13) {
$('#decryptAll button:button[name="submitDecryptAll"]').prop("disabled", true);
$('#decryptAll input:password[name="privateKeyPassword"]').prop("disabled", true);
OC.Encryption.decryptAll(privateKeyPassword);
@@ -240,21 +240,21 @@ $(document).ready(function(){
});
var uploadparms = {
- done: function(e, data) {
+ done: function (e, data) {
avatarResponseHandler(data.result);
}
};
- $('#uploadavatarbutton').click(function(){
+ $('#uploadavatarbutton').click(function () {
$('#uploadavatar').click();
});
$('#uploadavatar').fileupload(uploadparms);
- $('#selectavatar').click(function(){
+ $('#selectavatar').click(function () {
OC.dialogs.filepicker(
t('settings', "Select a profile picture"),
- function(path){
+ function (path) {
$.post(OC.generateUrl('/avatar/'), {path: path}, avatarResponseHandler);
},
false,
@@ -262,27 +262,27 @@ $(document).ready(function(){
);
});
- $('#removeavatar').click(function(){
+ $('#removeavatar').click(function () {
$.ajax({
- type: 'DELETE',
- url: OC.generateUrl('/avatar/'),
- success: function() {
+ type: 'DELETE',
+ url: OC.generateUrl('/avatar/'),
+ success: function () {
updateAvatar(true);
$('#removeavatar').hide();
}
});
});
- $('#abortcropperbutton').click(function(){
+ $('#abortcropperbutton').click(function () {
cleanCropper();
});
- $('#sendcropperbutton').click(function(){
+ $('#sendcropperbutton').click(function () {
sendCropData();
});
$('#pass2').strengthify({
- zxcvbn: OC.linkTo('3rdparty','zxcvbn/js/zxcvbn.js'),
+ zxcvbn: OC.linkTo('3rdparty', 'zxcvbn/js/zxcvbn.js'),
titles: [
t('core', 'Very weak password'),
t('core', 'Weak password'),
@@ -298,18 +298,58 @@ $(document).ready(function(){
'/avatar/{user}/{size}',
{user: OC.currentUser, size: 1}
) + '?requesttoken=' + oc_requesttoken;
- $.get(url, function(result) {
+ $.get(url, function (result) {
if (typeof(result) === 'object') {
$('#removeavatar').hide();
}
});
-} );
+
+ $('#sslCertificate').on('click', 'td.remove > img', function () {
+ var row = $(this).parent().parent();
+ $.post(OC.generateUrl('settings/ajax/removeRootCertificate'), {
+ cert: row.data('name')
+ });
+ row.remove();
+ return true;
+ });
+
+ $('#sslCertificate tr > td').tipsy({fade: true, gravity: 'n', live: true});
+
+ $('#rootcert_import').fileupload({
+ done: function (e, data) {
+ var issueDate = new Date(data.result.validFrom * 1000);
+ var expireDate = new Date(data.result.validTill * 1000);
+ var now = new Date();
+ var isExpired = !(issueDate <= now && now <= expireDate);
+
+ var row = $('<tr/>');
+ row.addClass(isExpired? 'expired': 'valid');
+ row.append($('<td/>').attr('title', data.result.organization).text(data.result.commonName));
+ row.append($('<td/>').attr('title', t('core,', 'Valid until {date}', {date: data.result.validFromString}))
+ .text(data.result.validTillString));
+ row.append($('<td/>').attr('title', data.result.issuerOrganization).text(data.result.issuer));
+ row.append($('<td/>').addClass('remove').append(
+ $('<img/>').attr({
+ alt: t('core', 'Delete'),
+ title: t('core', 'Delete'),
+ src: OC.imagePath('core', 'actions/delete.svg')
+ }).addClass('action')
+ ));
+
+ $('#sslCertificate tbody').append(row);
+ }
+ });
+
+ $('#rootcert_import_button').click(function () {
+ $('#rootcert_import').click();
+ });
+});
OC.Encryption = {
- decryptAll: function(password) {
+ decryptAll: function (password) {
var message = t('settings', 'Decrypting files... Please wait, this can take some time.');
OC.Encryption.msg.start('#decryptAll .msg', message);
- $.post('ajax/decryptall.php', {password:password}, function(data) {
+ $.post('ajax/decryptall.php', {password: password}, function (data) {
if (data.status === "error") {
OC.Encryption.msg.finished('#decryptAll .msg', data);
$('#decryptAll input:password[name="privateKeyPassword"]').prop("disabled", false);
@@ -320,10 +360,10 @@ OC.Encryption = {
});
},
- deleteKeys: function() {
+ deleteKeys: function () {
var message = t('settings', 'Delete encryption keys permanently.');
OC.Encryption.msg.start('#restoreBackupKeys .msg', message);
- $.post('ajax/deletekeys.php', null, function(data) {
+ $.post('ajax/deletekeys.php', null, function (data) {
if (data.status === "error") {
OC.Encryption.msg.finished('#restoreBackupKeys .msg', data);
$('#restoreBackupKeys button:button[name="submitDeleteKeys"]').prop("disabled", false);
@@ -334,10 +374,10 @@ OC.Encryption = {
});
},
- restoreKeys: function() {
+ restoreKeys: function () {
var message = t('settings', 'Restore encryption keys.');
OC.Encryption.msg.start('#restoreBackupKeys .msg', message);
- $.post('ajax/restorekeys.php', {}, function(data) {
+ $.post('ajax/restorekeys.php', {}, function (data) {
if (data.status === "error") {
OC.Encryption.msg.finished('#restoreBackupKeys .msg', data);
$('#restoreBackupKeys button:button[name="submitDeleteKeys"]').prop("disabled", false);
@@ -349,24 +389,24 @@ OC.Encryption = {
}
};
-OC.Encryption.msg={
- start:function(selector, msg){
- var spinner = '<img src="'+ OC.imagePath('core', 'loading-small.gif') +'">';
+OC.Encryption.msg = {
+ start: function (selector, msg) {
+ var spinner = '<img src="' + OC.imagePath('core', 'loading-small.gif') + '">';
$(selector)
- .html( msg + ' ' + spinner )
+ .html(msg + ' ' + spinner)
.removeClass('success')
.removeClass('error')
.stop(true, true)
.show();
},
- finished:function(selector, data){
- if( data.status === "success" ){
- $(selector).html( data.data.message )
+ finished: function (selector, data) {
+ if (data.status === "success") {
+ $(selector).html(data.data.message)
.addClass('success')
.stop(true, true)
.delay(3000);
- }else{
- $(selector).html( data.data.message ).addClass('error');
+ } else {
+ $(selector).html(data.data.message).addClass('error');
}
}
};
diff --git a/settings/personal.php b/settings/personal.php
index b290d6a959e..e6f53d62704 100644
--- a/settings/personal.php
+++ b/settings/personal.php
@@ -8,6 +8,7 @@
OC_Util::checkLoggedIn();
$defaults = new OC_Defaults(); // initialize themable default strings and urls
+$certificateManager = \OC::$server->getCertificateManager();
// Highlight navigation entry
OC_Util::addScript( 'settings', 'personal' );
@@ -98,6 +99,7 @@ $tmpl->assign('backupKeysExists' , $backupKeysExists);
$tmpl->assign('filesStillEncrypted' , $filesStillEncrypted);
$tmpl->assign('enableAvatars', \OC_Config::getValue('enable_avatars', true));
$tmpl->assign('avatarChangeSupported', OC_User::canUserChangeAvatar(OC_User::getUser()));
+$tmpl->assign('certs', $certificateManager->listCertificates());
$forms=OC_App::getForms('personal');
$tmpl->assign('forms', array());
diff --git a/settings/routes.php b/settings/routes.php
index 1c8ad1b3fe8..191b5febbd7 100644
--- a/settings/routes.php
+++ b/settings/routes.php
@@ -62,6 +62,10 @@ $this->create('settings_ajax_restorekeys', '/settings/ajax/restorekeys.php')
->actionInclude('settings/ajax/restorekeys.php');
$this->create('settings_ajax_deletekeys', '/settings/ajax/deletekeys.php')
->actionInclude('settings/ajax/deletekeys.php');
+$this->create('settings_cert_post', '/settings/ajax/addRootCertificate')
+ ->actionInclude('settings/ajax/addRootCertificate.php');
+$this->create('settings_cert_remove', '/settings/ajax/removeRootCertificate')
+ ->actionInclude('settings/ajax/removeRootCertificate.php');
// apps
$this->create('settings_ajax_apps_ocs', '/settings/ajax/apps/ocs.php')
->actionInclude('settings/ajax/apps/ocs.php');
diff --git a/settings/templates/personal.php b/settings/templates/personal.php
index c1fb20dce05..871a0ec9e39 100644
--- a/settings/templates/personal.php
+++ b/settings/templates/personal.php
@@ -2,7 +2,10 @@
* Copyright (c) 2011, Robin Appelman <icewind1991@gmail.com>
* This file is licensed under the Affero General Public License version 3 or later.
* See the COPYING-README file.
- */?>
+ */
+
+/** @var $_ array */
+?>
<div class="clientsbox center">
<h2><?php p($l->t('Get the apps to sync your files'));?></h2>
@@ -150,6 +153,44 @@ if($_['passwordChangeSupported']) {
print_unescaped($form);
};?>
+<div class="section">
+ <h2><?php p($l->t('SSL root certificates')); ?></h2>
+ <table id="sslCertificate" class="grid">
+ <thead>
+ <th><?php p($l->t('Common Name')); ?></th>
+ <th><?php p($l->t('Valid until')); ?></th>
+ <th><?php p($l->t('Issued By')); ?></th>
+ <th/>
+ </thead>
+ <tbody>
+ <?php foreach ($_['certs'] as $rootCert): /**@var \OCP\ICertificate $rootCert*/ ?>
+ <tr class="<?php echo ($rootCert->isExpired()) ? 'expired' : 'valid' ?>" data-name="<?php p($rootCert->getName()) ?>">
+ <td class="rootCert" title="<?php p($rootCert->getOrganization())?>">
+ <?php p($rootCert->getCommonName()) ?>
+ </td>
+ <td title="<?php p($l->t('Valid until %s', $l->l('date', $rootCert->getExpireDate()))) ?>">
+ <?php echo $l->l('date', $rootCert->getExpireDate()) ?>
+ </td>
+ <td title="<?php p($rootCert->getIssuerOrganization()) ?>">
+ <?php p($rootCert->getIssuerName()) ?>
+ </td>
+ <td <?php if ($rootCert != ''): ?>class="remove"
+ <?php else: ?>style="visibility:hidden;"
+ <?php endif; ?>><img alt="<?php p($l->t('Delete')); ?>"
+ title="<?php p($l->t('Delete')); ?>"
+ class="svg action"
+ src="<?php print_unescaped(image_path('core', 'actions/delete.svg')); ?>"/>
+ </td>
+ </tr>
+ <?php endforeach; ?>
+ </tbody>
+ </table>
+ <form class="uploadButton" method="post" action="<?php p(\OC_Helper::linkToRoute('settings_cert_post')); ?>" target="certUploadFrame">
+ <input type="file" id="rootcert_import" name="rootcert_import" class="hidden">
+ <input type="button" id="rootcert_import_button" value="<?php p($l->t('Import Root Certificate')); ?>"/>
+ </form>
+</div>
+
<?php if($_['enableDecryptAll']): ?>
<div class="section">
@@ -177,11 +218,8 @@ if($_['passwordChangeSupported']) {
</p>
<br />
</div>
-
<?php endif; ?>
-
-
<div id="restoreBackupKeys" <?php $_['backupKeysExists'] ? '' : print_unescaped("class='hidden'") ?>>
<?php p($l->t( "Your encryption keys are moved to a backup location. If something went wrong you can restore the keys. Only delete them permanently if you are sure that all files are decrypted correctly." )); ?>
diff --git a/tests/data/certificates/badCertificate.crt b/tests/data/certificates/badCertificate.crt
new file mode 100644
index 00000000000..dcb1895fbad
--- /dev/null
+++ b/tests/data/certificates/badCertificate.crt
@@ -0,0 +1,13 @@
+-----BEGIN CERTIFICATE-----
+MIICATCCAWoCCQDNdmb4pJrUeDANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJB
+VTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0
+cyBQdHkgTHRkMB4XDTE0MDgyNzA4NDg1MVoXDTE1MDgyNzA4NDg1MVowRTELMAkG
+A1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGEludGVybmV0
+IFdpZGdpdHMgUHR5IEx0ZDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAvrMe
+x5D45HVMV2U4kqTU0mzHAihHT6r+OtO6g7S9yIlJZGGVcEet6An78Ow7aYM141eI
+Jfbvqql7OIblHXSw7mvkw4bOQ1ee5lmJYOYCgaMNJ6mBLJfpK9xwidb0ZvhWOA8P
+DLIiBKA3T5ChXCzilD5GF2+H/BXBE9lL9tuDjM0CAwEAATANBgkqhkiG9w0BAQUF
+AAOBgQCJwfJe7j+aNkopw+P8uxobfOnMWU9XC4Pu+39TVLeakeSqu2Y8vJSHmkjF
+WK3VXAJr33Eul5VP/3SWGwuRPd9X4i4iLh1gJfYvi9MJf1lQNYncGCM+xtdrNu2O
+u0yexkOBRrapDYjcv58BiOaFgvFLquKvtVj9HlcYRfwfM77uKQ==
+-----END CERTIFICATE----- \ No newline at end of file
diff --git a/tests/data/certificates/expiredCertificate.crt b/tests/data/certificates/expiredCertificate.crt
new file mode 100644
index 00000000000..5e7e5df2cbf
--- /dev/null
+++ b/tests/data/certificates/expiredCertificate.crt
@@ -0,0 +1,13 @@
+-----BEGIN CERTIFICATE-----
+MIICATCCAWoCCQCjCIB6tCZ2sDANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJB
+VTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0
+cyBQdHkgTHRkMB4XDTE0MDgyNzA5MTI0M1oXDTE0MDgyODA5MTI0M1owRTELMAkG
+A1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGEludGVybmV0
+IFdpZGdpdHMgUHR5IEx0ZDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAvrMe
+x5D45HVMV2U4kqTU0mzHAihHT6r+OtO6g7S9yIlJZGGVcEet6An78Ow7aYM141eI
+Jfbvqql7OIblHXSw7mvkw4bOQ1ee5lmJYOYCgaMNJ6mBLJfpK9xwidb0ZvhWOA8P
+DLIiBKA3T5ChXCzilD5GF2+H/BXBE9lL9tuDjM0CAwEAATANBgkqhkiG9w0BAQUF
+AAOBgQBuNClmOj3wudlX86nygcZgQT2+ZS8f1iJgM9lbrrkenT6tgcT1/YjcrN9C
+BZR29Wz7htflpqverLUGZXh72K+gYercyR16Zu7zjt/NWuZldZmzJ3bUGq2HSoCX
+2sDykAEuaDxUlzdJrztlOH4vPlRaGbxUogpC2hB1BQfxA90CIA==
+-----END CERTIFICATE----- \ No newline at end of file
diff --git a/tests/data/certificates/goodCertificate.crt b/tests/data/certificates/goodCertificate.crt
new file mode 100644
index 00000000000..4a5d7bd32fe
--- /dev/null
+++ b/tests/data/certificates/goodCertificate.crt
@@ -0,0 +1,15 @@
+-----BEGIN CERTIFICATE-----
+MIICazCCAdQCCQCySF7HjQD78DANBgkqhkiG9w0BAQUFADB6MQswCQYDVQQGEwJD
+SDEPMA0GA1UECBMGWnVyaWNoMQ8wDQYDVQQHEwZadXJpY2gxFjAUBgNVBAoTDW93
+bkNsb3VkIEluYy4xETAPBgNVBAsTCFNlY3VyaXR5MR4wHAYDVQQDExVzZWN1cml0
+eS5vd25jbG91ZC5jb20wHhcNMTQwODI3MDg0NTUyWhcNMTUwODI3MDg0NTUyWjB6
+MQswCQYDVQQGEwJDSDEPMA0GA1UECBMGWnVyaWNoMQ8wDQYDVQQHEwZadXJpY2gx
+FjAUBgNVBAoTDW93bkNsb3VkIEluYy4xETAPBgNVBAsTCFNlY3VyaXR5MR4wHAYD
+VQQDExVzZWN1cml0eS5vd25jbG91ZC5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0A
+MIGJAoGBAL55lB4RvU0pTyh7YsLCxPBq43xxkRZBxfZENoflCIUsBo7/mXNz2zVO
+476oQ4L47heUOX3j8kemOgPmWEqA34JB8rusijCy5WqFBLnm4HsRLa66i+Jgd+Yl
+QhcKvhGas1K/CVTG4oSLoAmA2coZUL94uxnRtd8aluflHMNGApIlAgMBAAEwDQYJ
+KoZIhvcNAQEFBQADgYEADo08zWdOtIvCKFDnLbzRwIjSYTlAtQtQaULv7KQe3qIn
+iaFAi6fAynHfdC8/2tvmSeniw0OZBkrfVGIVtUbwCSrljNSUY/lWrUR0pE61lb4r
+DpX0JZjlk48XEaErRVDfu3wq6n/2nYg6HnaLOPwt8OSYYrxzvXlFPrKBH3q6R+M=
+-----END CERTIFICATE----- \ No newline at end of file
diff --git a/tests/lib/security/certificate.php b/tests/lib/security/certificate.php
new file mode 100644
index 00000000000..db33dd00d99
--- /dev/null
+++ b/tests/lib/security/certificate.php
@@ -0,0 +1,93 @@
+<?php
+/**
+ * Copyright (c) 2014 Lukas Reschke <lukas@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+use \OC\Security\Certificate;
+
+class CertificateTest extends \PHPUnit_Framework_TestCase {
+
+ /** @var Certificate That contains a valid certificate */
+ protected $goodCertificate;
+ /** @var Certificate That contains an invalid certificate */
+ protected $invalidCertificate;
+ /** @var Certificate That contains an expired certificate */
+ protected $expiredCertificate;
+
+ function setUp() {
+ $goodCertificate = file_get_contents(__DIR__ . '/../../data/certificates/goodCertificate.crt');
+ $this->goodCertificate = new Certificate($goodCertificate, 'GoodCertificate');
+ $badCertificate = file_get_contents(__DIR__ . '/../../data/certificates/badCertificate.crt');
+ $this->invalidCertificate = new Certificate($badCertificate, 'BadCertificate');
+ $expiredCertificate = file_get_contents(__DIR__ . '/../../data/certificates/expiredCertificate.crt');
+ $this->expiredCertificate = new Certificate($expiredCertificate, 'ExpiredCertificate');
+ }
+
+ /**
+ * @expectedException \Exception
+ * @expectedExceptionMessage Certificate could not get parsed.
+ */
+ function testBogusData() {
+ new Certificate('foo', 'bar');
+ }
+
+ function testGetName() {
+ $this->assertSame('GoodCertificate', $this->goodCertificate->getName());
+ $this->assertSame('BadCertificate', $this->invalidCertificate->getName());
+ }
+
+ function testGetCommonName() {
+ $this->assertSame('security.owncloud.com', $this->goodCertificate->getCommonName());
+ $this->assertSame(null, $this->invalidCertificate->getCommonName());
+ }
+
+ function testGetOrganization() {
+ $this->assertSame('ownCloud Inc.', $this->goodCertificate->getOrganization());
+ $this->assertSame('Internet Widgits Pty Ltd', $this->invalidCertificate->getOrganization());
+ }
+
+ function testGetSerial() {
+ $this->assertSame('7F:FF:FF:FF:FF:FF:FF:FF', $this->goodCertificate->getSerial());
+ $this->assertSame('7F:FF:FF:FF:FF:FF:FF:FF', $this->invalidCertificate->getSerial());
+ }
+
+ function testGetIssueDate() {
+ $expected = new DateTime('2014-08-27 08:45:52 GMT');
+ $this->assertEquals($expected->getTimestamp(), $this->goodCertificate->getIssueDate()->getTimestamp());
+ $expected = new DateTime('2014-08-27 08:48:51 GMT');
+ $this->assertEquals($expected->getTimestamp(), $this->invalidCertificate->getIssueDate()->getTimestamp());
+ }
+
+ function testGetExpireDate() {
+ $expected = new DateTime('2015-08-27 08:45:52 GMT');
+ $this->assertEquals($expected->getTimestamp(), $this->goodCertificate->getExpireDate()->getTimestamp());
+ $expected = new DateTime('2015-08-27 08:48:51 GMT');
+ $this->assertEquals($expected->getTimestamp(), $this->invalidCertificate->getExpireDate()->getTimestamp());
+ $expected = new DateTime('2014-08-28 09:12:43 GMT');
+ $this->assertEquals($expected->getTimestamp(), $this->expiredCertificate->getExpireDate()->getTimestamp());
+ }
+
+ /**
+ * Obviously the following test case might fail after 2015-08-27, just create a new certificate with longer validity then
+ */
+ function testIsExpired() {
+ $this->assertSame(false, $this->goodCertificate->isExpired());
+ $this->assertSame(false, $this->invalidCertificate->isExpired());
+ $this->assertSame(true, $this->expiredCertificate->isExpired());
+ }
+
+ function testGetIssuerName() {
+ $this->assertSame('security.owncloud.com', $this->goodCertificate->getIssuerName());
+ $this->assertSame(null, $this->invalidCertificate->getIssuerName());
+ $this->assertSame(null, $this->expiredCertificate->getIssuerName());
+ }
+
+ function testGetIssuerOrganization() {
+ $this->assertSame('ownCloud Inc.', $this->goodCertificate->getIssuerOrganization());
+ $this->assertSame('Internet Widgits Pty Ltd', $this->invalidCertificate->getIssuerOrganization());
+ $this->assertSame('Internet Widgits Pty Ltd', $this->expiredCertificate->getIssuerOrganization());
+ }
+}
diff --git a/tests/lib/security/certificatemanager.php b/tests/lib/security/certificatemanager.php
new file mode 100644
index 00000000000..5baf9e16e81
--- /dev/null
+++ b/tests/lib/security/certificatemanager.php
@@ -0,0 +1,87 @@
+<?php
+/**
+ * Copyright (c) 2014 Lukas Reschke <lukas@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+use \OC\Security\CertificateManager;
+
+class CertificateManagerTest extends \PHPUnit_Framework_TestCase {
+
+ /** @var CertificateManager */
+ private $certificateManager;
+ /** @var String */
+ private $username;
+ /** @var \OC\User\User */
+ private $user;
+
+ function setUp() {
+ $this->username = OC_Util::generateRandomBytes(20);
+ OC_User::createUser($this->username, OC_Util::generateRandomBytes(20));
+
+ \OC_Util::tearDownFS();
+ \OC_User::setUserId('');
+ \OC\Files\Filesystem::tearDown();
+ \OC_Util::setupFS($this->username);
+
+ $this->user = \OC::$server->getUserManager()->get($this->username);
+
+ $this->certificateManager = new CertificateManager($this->user);
+ }
+
+ function tearDown() {
+ \OC_User::deleteUser($this->username);
+ }
+
+ protected function assertEqualsArrays($expected, $actual) {
+ sort($expected);
+ sort($actual);
+
+ $this->assertEquals($expected, $actual);
+ }
+
+ function testListCertificates() {
+ // Test empty certificate bundle
+ $this->assertSame(array(), $this->certificateManager->listCertificates());
+
+ // Add some certificates
+ $this->certificateManager->addCertificate(file_get_contents(__DIR__.'/../../data/certificates/goodCertificate.crt'), 'GoodCertificate');
+ $certificateStore = array();
+ $certificateStore[] = new \OC\Security\Certificate(file_get_contents(__DIR__.'/../../data/certificates/goodCertificate.crt'), 'GoodCertificate');
+ $this->assertEqualsArrays($certificateStore, $this->certificateManager->listCertificates());
+
+ // Add another certificates
+ $this->certificateManager->addCertificate(file_get_contents(__DIR__.'/../../data/certificates/expiredCertificate.crt'), 'ExpiredCertificate');
+ $certificateStore[] = new \OC\Security\Certificate(file_get_contents(__DIR__.'/../../data/certificates/expiredCertificate.crt'), 'ExpiredCertificate');
+ $this->assertEqualsArrays($certificateStore, $this->certificateManager->listCertificates());
+ }
+
+ /**
+ * @expectedException \Exception
+ * @expectedExceptionMessage Certificate could not get parsed.
+ */
+ function testAddInvalidCertificate() {
+ $this->certificateManager->addCertificate('InvalidCertificate', 'invalidCertificate');
+ }
+
+ function testAddDangerousFile() {
+ $this->assertFalse($this->certificateManager->addCertificate(file_get_contents(__DIR__.'/../../data/certificates/expiredCertificate.crt'), '.htaccess'));
+ $this->assertFalse($this->certificateManager->addCertificate(file_get_contents(__DIR__.'/../../data/certificates/expiredCertificate.crt'), '../../foo.txt'));
+ }
+
+ function testRemoveDangerousFile() {
+ $this->assertFalse($this->certificateManager->removeCertificate('../../foo.txt'));
+ }
+
+ function testRemoveExistingFile() {
+ $this->certificateManager->addCertificate(file_get_contents(__DIR__.'/../../data/certificates/goodCertificate.crt'), 'GoodCertificate');
+ $this->assertTrue($this->certificateManager->removeCertificate('GoodCertificate'));
+ }
+
+ function testGetCertificateBundle() {
+ $this->assertSame($this->user->getHome().'/files_external/rootcerts.crt', $this->certificateManager->getCertificateBundle());
+ }
+
+} \ No newline at end of file