summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAleksander Machniak <machniak@kolabsys.com>2014-10-15 10:26:01 (GMT)
committerAleksander Machniak <machniak@kolabsys.com>2014-10-15 10:26:01 (GMT)
commit101e2cea2c35a8690df75de12cadb4c587f03031 (patch)
tree012b22f85d4f390b7bdeba710d18d81e861048c1
parent966d4917de4acfe60e486b141568681d6398bcfe (diff)
downloadkolab-chwala-101e2cea2c35a8690df75de12cadb4c587f03031.tar.gz
Add option to save (encrypted) passwords to external storages (#3784)
-rw-r--r--lib/api/folder_auth.php19
-rw-r--r--lib/api/folder_create.php10
-rw-r--r--lib/client/file_ui_client_main.php9
-rw-r--r--lib/drivers/kolab/kolab_file_storage.php19
-rw-r--r--lib/drivers/seafile/seafile_file_storage.php21
-rw-r--r--lib/file_api.php71
-rw-r--r--lib/file_storage.php15
-rw-r--r--lib/file_utils.php1
-rw-r--r--lib/locale/en_US.php2
-rw-r--r--public_html/js/files_api.js2
-rw-r--r--public_html/js/files_ui.js17
-rw-r--r--public_html/skins/default/style.css27
12 files changed, 181 insertions, 32 deletions
diff --git a/lib/api/folder_auth.php b/lib/api/folder_auth.php
index 3304cbb..f6b564c 100644
--- a/lib/api/folder_auth.php
+++ b/lib/api/folder_auth.php
@@ -55,11 +55,24 @@ class file_api_folder_auth extends file_api_common
$data = array_merge($data, $this->args);
$data = $driver->driver_validate($data);
- // save changed data (except password)
- unset($data['password']);
+ // optionally store (encrypted) passwords
+ if (!empty($data['password']) && rcube_utils::get_boolean((string) $this->args['store_passwords'])) {
+ $data['password'] = $this->api->encrypt($data['password']);
+ }
+ else {
+ unset($data['password']);
+ }
+
+ // save changed data
foreach (array_keys($meta['form']) as $key) {
if ($meta['form_values'][$key] != $data[$key]) {
- // @TODO: save current driver config
+ // update driver config
+ $data['title'] = $driver_config['title'];
+ $data['driver'] = $driver_config['driver'];
+ $data['enabled'] = 1;
+
+ $backend = $this->api->get_backend();
+ $backend->driver_update($this->args['folder'], $data);
break;
}
}
diff --git a/lib/api/folder_create.php b/lib/api/folder_create.php
index 55b3b3a..69767ba 100644
--- a/lib/api/folder_create.php
+++ b/lib/api/folder_create.php
@@ -82,9 +82,13 @@ class file_api_folder_create extends file_api_common
$data['driver'] = $this->args['driver'];
$data['enabled'] = 1;
- // don't store password
- // @TODO: store passwords encrypted?
- unset($data['password']);
+ // optionally store (encrypted) passwords
+ if (!empty($data['password']) && rcube_utils::get_boolean((string) $this->args['store_passwords'])) {
+ $data['password'] = $this->api->encrypt($data['password']);
+ }
+ else {
+ unset($data['password']);
+ }
// save the mount point info in config
$backend->driver_create($data);
diff --git a/lib/client/file_ui_client_main.php b/lib/client/file_ui_client_main.php
index bc78220..3b882f7 100644
--- a/lib/client/file_ui_client_main.php
+++ b/lib/client/file_ui_client_main.php
@@ -71,10 +71,19 @@ class file_ui_client_main extends file_ui
'value' => '1',
'id' => 'folder-driver-checkbox',
));
+ $drivers_pass_input = new html_checkbox(array(
+ 'name' => 'store_passwords',
+ 'value' => '1',
+ 'id' => 'folder-driver-pass-checkbox',
+ ));
$drivers = html::div('drivers',
html::span('drivers-header', $drivers_input->show() . '&nbsp;'
. html::label('folder-driver-checkbox', $this->translate('folder.driverselect')))
. html::div('drivers-list', '')
+ . html::div('drivers-footer', $drivers_pass_input->show() . '&nbsp;'
+ . html::label('folder-driver-pass-checkbox', $this->translate('folder.driverwithpass'))
+ . html::span('description', $this->translate('folder.driverwithpassdesc'))
+ )
);
$table = new html_table;
diff --git a/lib/drivers/kolab/kolab_file_storage.php b/lib/drivers/kolab/kolab_file_storage.php
index 70a7f43..a8987e9 100644
--- a/lib/drivers/kolab/kolab_file_storage.php
+++ b/lib/drivers/kolab/kolab_file_storage.php
@@ -101,6 +101,19 @@ class kolab_file_storage implements file_storage
}
/**
+ * Get password and name of authenticated user
+ *
+ * @return array Authenticated user data
+ */
+ public function auth_info()
+ {
+ return array(
+ 'username' => $_SESSION['username'],
+ 'password' => $this->rc->decrypt($_SESSION['password']),
+ );
+ }
+
+ /**
* Storage host selection
*/
private function select_host($username)
@@ -368,16 +381,16 @@ class kolab_file_storage implements file_storage
/**
* Update configuration of external driver (mount point)
*
- * @param string $name Driver instance name
+ * @param string $title Driver instance title
* @param array $driver Driver data
*
* @throws Exception
*/
- public function driver_update($name, $driver)
+ public function driver_update($title, $driver)
{
$drivers = $this->driver_list();
- if (!$drivers[$name]) {
+ if (!$drivers[$title]) {
throw new Exception("Driver not found", file_storage::ERROR);
}
diff --git a/lib/drivers/seafile/seafile_file_storage.php b/lib/drivers/seafile/seafile_file_storage.php
index 6463ea1..6343b7d 100644
--- a/lib/drivers/seafile/seafile_file_storage.php
+++ b/lib/drivers/seafile/seafile_file_storage.php
@@ -90,6 +90,19 @@ class seafile_file_storage implements file_storage
}
/**
+ * Get password and name of authenticated user
+ *
+ * @return array Authenticated user data
+ */
+ public function auth_info()
+ {
+ return array(
+ 'username' => $_SESSION[$this->title . 'seafile_user'],
+ 'password' => $this->rc->decrypt($_SESSION[$this->title . 'seafile_pass']),
+ );
+ }
+
+ /**
* Initialize SeaFile Web API connection
*/
protected function init($skip_auth = false)
@@ -210,11 +223,11 @@ class seafile_file_storage implements file_storage
/**
* Delete configuration of external driver (mount point)
*
- * @param string $name Driver instance name
+ * @param string $title Driver instance name
*
* @throws Exception
*/
- public function driver_delete($name)
+ public function driver_delete($title)
{
throw new Exception("Not implemented", file_storage::ERROR_UNSUPPORTED);
}
@@ -233,12 +246,12 @@ class seafile_file_storage implements file_storage
/**
* Update configuration of external driver (mount point)
*
- * @param string $name Driver instance name
+ * @param string $title Driver instance name
* @param array $driver Driver data
*
* @throws Exception
*/
- public function driver_update($name, $driver)
+ public function driver_update($title, $driver)
{
throw new Exception("Not implemented", file_storage::ERROR_UNSUPPORTED);
}
diff --git a/lib/file_api.php b/lib/file_api.php
index 97cb4dc..d245e2d 100644
--- a/lib/file_api.php
+++ b/lib/file_api.php
@@ -24,7 +24,9 @@
class file_api extends file_locale
{
- const ERROR_CODE = 500;
+ const ERROR_CODE = 500;
+ const ERROR_INVALID = 501;
+
const OUTPUT_JSON = 'application/json';
const OUTPUT_HTML = 'text/html';
@@ -284,7 +286,7 @@ class file_api extends file_locale
}
}
- throw new Exception("Unknown method", 501);
+ throw new Exception("Unknown method", self::ERROR_INVALID);
}
/**
@@ -344,6 +346,8 @@ class file_api extends file_locale
foreach ((array) $preconf as $title => $item) {
if (!in_array($title, $all)) {
$item['title'] = $title;
+ $item['admin'] = true;
+
$result[] = $as_objects ? $this->get_driver_object($item) : $item;
}
}
@@ -396,9 +400,13 @@ class file_api extends file_locale
$this->drivers[$key] = $driver = $this->load_driver_object($config['driver']);
if ($config['username'] == '%u') {
- $rcube = rcube::get_instance();
- $config['username'] = $_SESSION['user'];
- $config['password'] = $rcube->decrypt($_SESSION['password']);
+ $backend = $this->get_backend();
+ $auth_info = $backend->auth_info();
+ $config['username'] = $auth_info['username'];
+ $config['password'] = $auth_info['password'];
+ }
+ else if (!empty($config['password']) && empty($config['admin'])) {
+ $config['password'] = $this->decrypt($config['password']);
}
// configure api
@@ -647,4 +655,57 @@ class file_api extends file_locale
return $str;
}
+
+ /**
+ * Encrypts data with current user password
+ *
+ * @param string $str A string to encrypt
+ *
+ * @return string Encrypted string (and base64-encoded)
+ */
+ public function encrypt($str)
+ {
+ $rcube = rcube::get_instance();
+ $key = $this->get_crypto_key();
+
+ return $rcube->encrypt($str, $key, true);
+ }
+
+ /**
+ * Decrypts data encrypted with encrypt() method
+ *
+ * @param string $str Encrypted string (base64-encoded)
+ *
+ * @return string Decrypted string
+ */
+ public function decrypt($str)
+ {
+ $rcube = rcube::get_instance();
+ $key = $this->get_crypto_key();
+
+ return $rcube->decrypt($str, $key, true);
+ }
+
+ /**
+ * Set encryption password
+ */
+ protected function get_crypto_key()
+ {
+ $key = 'chwala_crypto_key';
+ $backend = $this->get_backend();
+ $user = $backend->auth_info();
+ $password = $user['password'] . $user['username'];
+
+ // encryption password must be 24 characters, no less, no more
+ if (($len = strlen($password)) > 24) {
+ $password = substr($password, 0, 24);
+ }
+ else {
+ $password = $password . substr($this->conf->get('des_key'), 0, 24 - $len);
+ }
+
+ $this->conf->set($key, $password);
+
+ return $key;
+ }
}
diff --git a/lib/file_storage.php b/lib/file_storage.php
index 7abb20f..d2b9c55 100644
--- a/lib/file_storage.php
+++ b/lib/file_storage.php
@@ -59,6 +59,13 @@ interface file_storage
public function authenticate($username, $password);
/**
+ * Get password and name of authenticated user
+ *
+ * @return array Authenticated user data
+ */
+ public function auth_info();
+
+ /**
* Configures environment
*
* @param array $config Configuration
@@ -92,11 +99,11 @@ interface file_storage
/**
* Delete configuration of external driver (mount point)
*
- * @param string $name Driver instance name
+ * @param string $title Driver instance title
*
* @throws Exception
*/
- public function driver_delete($name);
+ public function driver_delete($title);
/**
* Return list of registered drivers (mount points)
@@ -126,12 +133,12 @@ interface file_storage
/**
* Update configuration of external driver (mount point)
*
- * @param string $name Driver instance name
+ * @param string $title Driver instance title
* @param array $driver Driver data
*
* @throws Exception
*/
- public function driver_update($name, $driver);
+ public function driver_update($title, $driver);
/**
* Create a file.
diff --git a/lib/file_utils.php b/lib/file_utils.php
index 416d151..eebd53a 100644
--- a/lib/file_utils.php
+++ b/lib/file_utils.php
@@ -71,6 +71,7 @@ class file_utils
// list of known file extensions, more in Roundcube config
static $ext_map = array(
'doc' => 'application/msword',
+ 'eml' => 'message/rfc822',
'gz' => 'application/gzip',
'htm' => 'text/html',
'html' => 'text/html',
diff --git a/lib/locale/en_US.php b/lib/locale/en_US.php
index 192d3b0..3b1bf7c 100644
--- a/lib/locale/en_US.php
+++ b/lib/locale/en_US.php
@@ -37,6 +37,8 @@ $LANG['folder.edit'] = 'Edit';
$LANG['folder.edittitle'] = 'Edit Folder';
$LANG['folder.under'] = 'inside the current folder';
$LANG['folder.driverselect'] = 'bind with the external storage';
+$LANG['folder.driverwithpass'] = 'remember password';
+$LANG['folder.driverwithpassdesc'] = 'Stored passwords will be encrypted. Enable this if you do not want to be asked for the password on every login or you want this storage to be available via WebDAV.';
$LANG['folder.name'] = 'Name:';
$LANG['folder.authenticate'] = 'Logon to $title';
diff --git a/public_html/js/files_api.js b/public_html/js/files_api.js
index b5a3408..76df8a7 100644
--- a/public_html/js/files_api.js
+++ b/public_html/js/files_api.js
@@ -119,7 +119,7 @@ function files_api()
this.request = function(action, data, func)
{
// Use POST for modification actions with probable big request size
- var method = /(create|delete|move|copy|update)/.test(action) ? 'post' : 'get';
+ var method = /(create|delete|move|copy|update|auth)/.test(action) ? 'post' : 'get';
return this[method](action, data, func);
};
diff --git a/public_html/js/files_ui.js b/public_html/js/files_ui.js
index ab397f5..fc985e4 100644
--- a/public_html/js/files_ui.js
+++ b/public_html/js/files_ui.js
@@ -1024,7 +1024,7 @@ function files_ui()
// create dialog for user credentials of external storage
this.folder_list_auth_dialog = function(label, driver)
{
- var buttons = {},
+ var div, buttons = {},
content = this.folder_list_auth_form(driver),
title = this.t('folder.authenticate').replace('$title', label);
@@ -1032,7 +1032,7 @@ function files_ui()
var data = {folder: label, list: 1};
$('input', this.modal).each(function() {
- data[this.name] = this.value;
+ data[this.name] = this.type == 'checkbox' && !this.checked ? '' : this.value;
});
ui.open_dialog = this;
@@ -1047,6 +1047,14 @@ function files_ui()
ui.folder_list_auth_errors();
};
+ // copy "remember password" checkbox into the dialog
+ div = $('.drivers-footer').clone();
+ if (div.length) {
+ div.find('input').each(function() { this.id += '-dialog'; });
+ div.find('label').each(function() { $(this).prop('for', $(this).prop('for') + '-dialog'); });
+ content.append(div.show());
+ }
+
this.modal_dialog(content, buttons, {
title: title,
fxOpen: function(win) {
@@ -1578,7 +1586,9 @@ function files_ui()
folder = this.env.folder + this.env.directory_separator;
}
else if (data.external && data.driver) {
+ args.store_passwords = data.store_passwords;
args.driver = data.driver;
+
$.each(data, function(i, v) {
if (i.startsWith(data.driver + '[')) {
args[i.substring(data.driver.length + 1, i.length - 1)] = v;
@@ -1658,6 +1668,7 @@ function files_ui()
$('#folder-driver-checkbox').change(function() {
drivers_list[this.checked ? 'show' : 'hide']();
+ $('.drivers-footer')[this.checked ? 'show' : 'hide']();
ref.folder_types_init();
});
@@ -1677,7 +1688,7 @@ function files_ui()
list[0].click();
}
- $('.drivers-list')[list.length && $('#folder-driver-checkbox:checked').length ? 'show' : 'hide']();
+ $('.drivers-list,.drivers-footer')[list.length && $('#folder-driver-checkbox:checked').length ? 'show' : 'hide']();
ref.form_show('folder-create');
};
diff --git a/public_html/skins/default/style.css b/public_html/skins/default/style.css
index e6a6173..8169e8a 100644
--- a/public_html/skins/default/style.css
+++ b/public_html/skins/default/style.css
@@ -29,6 +29,11 @@ textarea {
color: black;
}
+input[type="checkbox"],
+input[type="radio"] {
+ vertical-align: middle;
+}
+
select[multiple="multiple"] {
padding-left: 0;
}
@@ -1390,11 +1395,6 @@ td span.branch span.l3
font-weight: bold;
}
-.drivers-list .description {
- font-size: 9px;
- color: #666;
-}
-
.drivers-list img {
vertical-align: middle;
background-color: #e0e0e0;
@@ -1414,16 +1414,31 @@ td span.branch span.l3
width: 400px;
}
+.drivers-footer .description,
+.drivers-list .description {
+ font-size: 10px;
+ color: #666;
+ display: block;
+}
+
+.drivers-footer .description {
+ margin-left: 25px;
+}
+
div.form .formrow {
display: block;
padding: 1px;
}
-div.form label {
+div.form .formrow label {
width: 80px;
display: inline-block;
}
+div.form .drivers-footer {
+ margin-top: 10px;
+}
+
/****** File open interface elements ******/