Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F9121964
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
18 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/plugins/owncloud/README b/plugins/owncloud/README
new file mode 100644
index 00000000..ecf66f3e
--- /dev/null
+++ b/plugins/owncloud/README
@@ -0,0 +1,78 @@
+Connecting Roundcube with ownCloud
+==================================
+
+ATTENTION: this is just a proof-of-concept and should be be considered for
+production use.
+
+This plugin integrates ownCloud into the Roundcube web UI using iframes.
+Beside giving the user one single location to log-in and get access to all
+his/her data, the integration should also allow one to pick files from
+ownCloud when sending email messages and to store email attachments directly
+to the cloud.
+
+
+HOW IT WORKS
+------------
+
+The plugin embeds the web UI for ownCloud though an iframe into the Roundcube
+user interface. When accessing ownCloud though Roundcube, the user's login
+credentials (username + password) will be handed over to ownCloud in order to
+automatically authenticate the current Roundcube user to ownCloud. This
+exchange happens with HTTP requests directley between the ownCloud server and
+the Roundcube server using signed URLs to prevent from interception.
+
+IMPRTANT: The automatic user authentication only works if both apps use the
+same authentication backend. In the Kolab realm this is LDAP. You therefore
+need to configure the LDAP user authentication backend in ownCloud.
+
+Once authenticated, the user sees the ownCloud file manager within Roundcube
+and can do whether he/she is authorized to do. When composing a new email
+message in Roundcube, an addition button appear to select files from the
+ownCloud storage. (this feaure is not yet fully implemented).
+
+When terminating the session in Roundcube by pressing the Logout button, an
+according message is sent to ownCloud to termiate the user's session there, too.
+
+
+REQUIREMENTS
+------------
+
+* Roundcube version 0.9-beta or higher
+* ownCloud version 4.5 or higher
+* both apps running on the same host (due to browser's XSS protection)
+* both IMAP and ownCloud authenticate users on the same backend
+
+
+INSTALLATION
+------------
+
+Install this plugin in Roundcube and enable it.
+
+Rsync the contents of the copy_to_owncloud folder to the ownCloud installation.
+This will add a new app named "kolab_auth" and a theme "kolab" to ownCloud.
+
+
+Roundcube CONFIGURATION
+-----------------------
+
+Copy the config.inc.php.dist file to config.inc.php in Roundcube's owncloud
+plugin directory. Then edit the file and set the absolute URL where ownCloud
+is accessible and define a secret string for the authentication credential
+exchange between the swo systems.
+
+
+onwCloud CONFIGURATION
+----------------------
+
+Add the following lines to the ownCloud config array:
+
+ 'theme' => 'kolab',
+ 'kolaburl' => '<url-to-roundcube>',
+ 'kolabsecret' => '<shared-secret-string>',
+
+The value for 'kolabsecret' has to match the 'owncloud_secret' string in
+the Roundcube owncloud plugin configuration.
+
+Log-in to ownCloud as an administrator and enable the kolab_auth app.
+
+
diff --git a/plugins/owncloud/config.inc.php.dist b/plugins/owncloud/config.inc.php.dist
index c9e9d231..3d1f2ac7 100644
--- a/plugins/owncloud/config.inc.php.dist
+++ b/plugins/owncloud/config.inc.php.dist
@@ -1,4 +1,8 @@
<?php
// ownCloud URL
$rcmail_config['owncloud_url'] = 'https://owncloud.webmail.tld';
+
+// authentication exchange secret
+// has to be the same as the value for 'kolabsecret' in owncloud config
+$rcmail_config['owncloud_secret'] = '<shared-secret-string>';
diff --git a/plugins/owncloud/copy_to_owncload/apps/kolab_auth/appinfo/app.php b/plugins/owncloud/copy_to_owncload/apps/kolab_auth/appinfo/app.php
new file mode 100644
index 00000000..02e3e151
--- /dev/null
+++ b/plugins/owncloud/copy_to_owncload/apps/kolab_auth/appinfo/app.php
@@ -0,0 +1,51 @@
+<?php
+
+/*
+ Requires the following options in ownCloud config:
+
+ 'kolaburl' => 'https://<kolab-host>/<webclient-url>',
+ 'kolabsecret' => '<a secret key, the same as in Roundcube owncloud plugin>',
+
+*/
+
+
+// check for kolab auth token
+if (!OC_User::isLoggedIn() && !empty($_GET['kolab_auth'])) {
+ OCP\Util::writeLog('kolab_auth', 'got kolab auth token', OCP\Util::INFO);
+
+ // decode auth data from Roundcube
+ parse_str(oc_kolab_decode($_GET['kolab_auth']), $request);
+
+ // send back as POST request with session cookie
+ $postdata = http_build_query($request, '', '&');
+
+ // add request signature using secret key
+ $postdata .= '&hmac=' . hash_hmac('sha256', $postdata, OC_Config::getValue('kolabsecret', '<da-sso-secret-key>'));
+
+ $context = stream_context_create(array(
+ 'http' => array(
+ 'method' => 'POST',
+ 'header'=> "Content-type: application/x-www-form-urlencoded\r\n"
+ . "Content-Length: " . strlen($postdata) . "\r\n"
+ . "Cookie: " . $request['cname'] . '=' . $request['session'] . "\r\n",
+ 'content' => $postdata,
+ )
+ )
+ );
+
+ $url = !empty($_SERVER['HTTP_REFERER']) ? dirname($_SERVER['HTTP_REFERER']) . '/' : OC_Config::getValue('kolaburl', '');
+ $auth = @json_decode(file_get_contents($url . '?_action=owncloudsso', false, $context), true);
+
+ // fake HTTP authentication with user credentials received from Roundcube
+ if ($auth['user'] && $auth['pass']) {
+ $_SERVER['PHP_AUTH_USER'] = $auth['user'];
+ $_SERVER['PHP_AUTH_PW'] = $auth['pass'];
+ }
+}
+
+function oc_kolab_decode($str)
+{
+ // TODO: chose a more sophisticated encryption method
+ return base64_decode(str_pad(strrev($str), strlen($str) % 4, '=', STR_PAD_RIGHT));
+}
+
diff --git a/plugins/owncloud/copy_to_owncload/apps/kolab_auth/appinfo/info.xml b/plugins/owncloud/copy_to_owncload/apps/kolab_auth/appinfo/info.xml
new file mode 100644
index 00000000..aad9cea7
--- /dev/null
+++ b/plugins/owncloud/copy_to_owncload/apps/kolab_auth/appinfo/info.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0"?>
+<info>
+ <id>kolab_auth</id>
+ <name>Kolab user authentication</name>
+ <description>Allow to authenticate an existing Kolab web client session</description>
+ <licence>AGPL</licence>
+ <author>Thomas Bruederli</author>
+ <require>4.9</require>
+ <shipped>true</shipped>
+ <types>
+ <prelogin/>
+ </types>
+</info>
diff --git a/plugins/owncloud/copy_to_owncload/apps/kolab_auth/appinfo/version b/plugins/owncloud/copy_to_owncload/apps/kolab_auth/appinfo/version
new file mode 100644
index 00000000..6e8bf73a
--- /dev/null
+++ b/plugins/owncloud/copy_to_owncload/apps/kolab_auth/appinfo/version
@@ -0,0 +1 @@
+0.1.0
diff --git a/plugins/owncloud/copy_to_owncload/themes/kolab/core/css/styles.css b/plugins/owncloud/copy_to_owncload/themes/kolab/core/css/styles.css
new file mode 100755
index 00000000..290721bc
--- /dev/null
+++ b/plugins/owncloud/copy_to_owncload/themes/kolab/core/css/styles.css
@@ -0,0 +1,15 @@
+
+#content,
+#controls,
+#navigation {
+ top: 0px;
+}
+
+#navigation #settings {
+ bottom: 0px;
+}
+
+#leftcontent, .leftcontent,
+#rightcontent, .rightcontent {
+ top: 2.9em;
+}
\ No newline at end of file
diff --git a/plugins/owncloud/copy_to_owncload/themes/kolab/core/js/kolab.js b/plugins/owncloud/copy_to_owncload/themes/kolab/core/js/kolab.js
new file mode 100644
index 00000000..f5204567
--- /dev/null
+++ b/plugins/owncloud/copy_to_owncload/themes/kolab/core/js/kolab.js
@@ -0,0 +1,58 @@
+
+function kolab_connector()
+{
+ var remote;
+
+ // public members
+ this.window = window;
+
+ // export public methods
+ this.init = init;
+ this.init_picker = init_picker;
+ this.list_files = list_files;
+
+ function init(rcube)
+ {
+ remote = rcube;
+ }
+
+ function init_picker(rcube)
+ {
+ remote = rcube;
+
+ if (window.FileActions) {
+ // reset already registered actions
+ // FileActions.actions.file = {};
+
+ FileActions.register('file','Pick', OC.PERMISSION_READ, '', function(filename){
+ var dir = $('#dir').val();
+ remote.file_picked(dir, filename);
+ });
+ FileActions.setDefault('file', 'Pick');
+ }
+ }
+
+ function list_files()
+ {
+ var files = [];
+ $('#fileList tr').each(function(item){
+ var row = $(item),
+ type = row.attrib('data-type'),
+ file = row.attrib('data-file'),
+ mime = row.attrib('data-mime');
+
+ if (type == 'file') {
+ files.push(file);
+ }
+ });
+
+ return files;
+ }
+}
+
+$(document).ready(function(){
+ // connect with Roundcube running in parent window
+ if (window.parent && parent.rcmail && parent.rcube_owncloud) {
+ parent.rcube_owncloud.connect(new kolab_connector());
+ }
+});
\ No newline at end of file
diff --git a/plugins/owncloud/copy_to_owncload/themes/kolab/core/templates/layout.user.php b/plugins/owncloud/copy_to_owncload/themes/kolab/core/templates/layout.user.php
new file mode 100644
index 00000000..a588eacb
--- /dev/null
+++ b/plugins/owncloud/copy_to_owncload/themes/kolab/core/templates/layout.user.php
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title><?php echo isset($_['application']) && !empty($_['application'])?$_['application'].' | ':'' ?>ownCloud <?php echo OC_User::getUser()?' ('.OC_User::getUser().') ':'' ?></title>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <link rel="shortcut icon" href="<?php echo image_path('', 'favicon.png'); ?>" /><link rel="apple-touch-icon-precomposed" href="<?php echo image_path('', 'favicon-touch.png'); ?>" />
+ <?php foreach($_['cssfiles'] as $cssfile): ?>
+ <link rel="stylesheet" href="<?php echo $cssfile; ?>" type="text/css" media="screen" />
+ <?php endforeach; ?>
+ <script type="text/javascript">
+ var oc_webroot = '<?php echo OC::$WEBROOT; ?>';
+ var oc_appswebroots = <?php echo $_['apps_paths'] ?>;
+ var oc_current_user = '<?php echo OC_User::getUser() ?>';
+ var oc_requesttoken = '<?php echo $_['requesttoken']; ?>';
+ var oc_requestlifespan = '<?php echo $_['requestlifespan']; ?>';
+ </script>
+ <?php foreach($_['jsfiles'] as $jsfile): ?>
+ <script type="text/javascript" src="<?php echo $jsfile; ?>"></script>
+ <?php endforeach; ?>
+ <script type="text/javascript" src="<?php echo OC::$WEBROOT.'/themes/kolab/core/js/kolab.js'; ?>"></script>
+ <?php foreach($_['headers'] as $header): ?>
+ <?php
+ echo '<'.$header['tag'].' ';
+ foreach($header['attributes'] as $name=>$value) {
+ echo "$name='$value' ";
+ };
+ echo '/>';
+ ?>
+ <?php endforeach; ?>
+ </head>
+
+ <body id="<?php echo $_['bodyid'];?>">
+ <nav><div id="navigation">
+ <ul id="apps" class="svg">
+ <?php foreach($_['navigation'] as $entry): ?>
+ <li data-id="<?php echo $entry['id']; ?>"><a style="background-image:url(<?php echo $entry['icon']; ?>)" href="<?php echo $entry['href']; ?>" title="" <?php if( $entry['active'] ): ?> class="active"<?php endif; ?>><?php echo $entry['name']; ?></a>
+ </li>
+ <?php endforeach; ?>
+ </ul>
+
+ <ul id="settings" class="svg">
+ <img role=button tabindex=0 id="expand" class="svg" alt="<?php echo $l->t('Settings');?>" src="<?php echo image_path('', 'actions/settings.svg'); ?>" />
+ <span><?php echo $l->t('Settings');?></span>
+ <div id="expanddiv" <?php if($_['bodyid'] == 'body-user') echo 'style="display:none;"'; ?>>
+ <?php foreach($_['settingsnavigation'] as $entry):?>
+ <li><a style="background-image:url(<?php echo $entry['icon']; ?>)" href="<?php echo $entry['href']; ?>" title="" <?php if( $entry["active"] ): ?> class="active"<?php endif; ?>><?php echo $entry['name'] ?></a></li>
+ <?php endforeach; ?>
+ </div>
+ </ul>
+ </div></nav>
+
+ <div id="content">
+ <?php echo $_['content']; ?>
+ </div>
+ </body>
+</html>
diff --git a/plugins/owncloud/owncloud.js b/plugins/owncloud/owncloud.js
new file mode 100644
index 00000000..f52e25c1
--- /dev/null
+++ b/plugins/owncloud/owncloud.js
@@ -0,0 +1,94 @@
+/**
+ *
+ */
+function rcube_owncloud()
+{
+ this.cloud;
+ this.dialog;
+}
+
+// singleton getter
+rcube_owncloud.instance = function()
+{
+ if (!rcube_owncloud._instance) {
+ rcube_owncloud._instance = new rcube_owncloud;
+ }
+
+ return rcube_owncloud._instance;
+};
+
+// remote connector
+rcube_owncloud.connect = function(client)
+{
+ rcube_owncloud.instance().connect(client);
+};
+
+rcube_owncloud.prototype = {
+
+// callback from the ownCloud connector script loaded in the iframe
+connect: function(client)
+{
+ var me = this;
+ this.cloud = client;
+
+ if (this.dialog)
+ client.init_picker(this);
+},
+
+// callback function from FileAction in ownCloud widget
+file_picked: function(dir, file)
+{
+ alert('Picked: ' + dir + '/' + file + '\n\nTODO: get direct access URL and paste it to the email message body.');
+
+ if (this.dialog && this.dialog.is(':ui-dialog'))
+ this.dialog.dialog('close');
+},
+
+// open a dialog showing the ownCloud widget
+open_dialog: function(html)
+{
+ // request iframe code from server
+ if (!this.dialog && !html) {
+ var me = this;
+ rcmail.addEventListener('plugin.owncloudembed', function(html){ me.open_dialog(html); });
+ rcmail.http_request('owncloud/embed');
+ return;
+ }
+ // create jQuery dialog with iframe
+ else if (html) {
+ var height = $(window).height() * 0.8;
+ this.dialog = $('<div>')
+ .addClass('owncloudembed')
+ .css({ width:'100%', height:height+'px' })
+ .appendTo(document.body)
+ .html(html);
+
+ this.dialog.dialog({
+ modal: true,
+ autoOpen: false,
+ title: 'Select a file from the cloud',
+ width: '80%',
+ height: height
+ });
+ }
+
+ // open the dialog
+ if (this.dialog && this.dialog.is(':ui-dialog'))
+ this.dialog.dialog('open');
+}
+
+};
+
+// Roundcube startup
+window.rcmail && rcmail.addEventListener('init', function(){
+ // add a button for ownCloud file selection to compose screen
+ if (rcmail.env.action == 'compose') {
+ $('<a>')
+ .addClass('button owncloudcompose')
+ .html('ownCloud')
+ .click(function(){ rcube_owncloud.instance().open_dialog(); return false; })
+ .insertAfter('#compose-attachments input.button')
+ .before(' ');
+ }
+})
+
diff --git a/plugins/owncloud/owncloud.php b/plugins/owncloud/owncloud.php
index f43177a9..f48dca9c 100644
--- a/plugins/owncloud/owncloud.php
+++ b/plugins/owncloud/owncloud.php
@@ -1,79 +1,135 @@
<?php
/**
* OwnCloud Plugin
*
* @author Aleksander 'A.L.E.C' Machniak <machniak@kolabsys.com>
+ * @author Thomas Bruederli <bruederli@kolabsys.com>
* @licence GNU AGPL
*
* Configuration (see config.inc.php.dist)
*
*/
class owncloud extends rcube_plugin
{
- // all task excluding 'login' and 'logout'
- public $task = '?(?!login|logout).*';
- // we've got no ajax handlers
- public $noajax = true;
+ // all task excluding 'login'
+ public $task = '?(?!login).*';
// skip frames
public $noframe = true;
function init()
{
- $rcmail = rcube::get_instance();
-
// requires kolab_auth plugin
if (empty($_SESSION['kolab_uid'])) {
- return;
+ $_SESSION['kolab_uid'] = 'tb';
+ # return;
}
+ $rcmail = rcube::get_instance();
+
$this->add_texts('localization/', false);
// register task
$this->register_task('owncloud');
// register actions
$this->register_action('index', array($this, 'action'));
- $this->register_action('redirect', array($this, 'redirect'));
+ $this->register_action('embed', array($this, 'embed'));
+ $this->add_hook('session_destroy', array($this, 'logout'));
+
+ // handler for sso requests sent by the owncloud kolab_auth app
+ if ($rcmail->action == 'owncloudsso' && !empty($_POST['token'])) {
+ $this->add_hook('startup', array($this, 'sso_request'));
+ }
// add taskbar button
$this->add_button(array(
'command' => 'owncloud',
'class' => 'button-owncloud',
'classsel' => 'button-owncloud button-selected',
'innerclass' => 'button-inner',
'label' => 'owncloud.owncloud',
), 'taskbar');
// add style for taskbar button (must be here) and Help UI
$this->include_stylesheet($this->local_skin_path()."/owncloud.css");
+
+ if ($rcmail->task == 'owncloud' || $rcmail->action == 'compose') {
+ $this->include_script('owncloud.js');
+ }
}
function action()
{
$rcmail = rcube::get_instance();
$rcmail->output->add_handlers(array('owncloudframe' => array($this, 'frame')));
$rcmail->output->set_pagetitle($this->gettext('owncloud'));
$rcmail->output->send('owncloud.owncloud');
}
+ function embed()
+ {
+ $rcmail = rcmail::get_instance();
+ $rcmail->output->command('plugin.owncloudembed', $this->frame());
+ $rcmail->output->send();
+ }
+
function frame()
{
$rcmail = rcube::get_instance();
-
$this->load_config();
+ // generate SSO auth token
+ if (empty($_SESSION['owncloudauth']))
+ $_SESSION['owncloudauth'] = md5('ocsso' . $_SESSION['user_id'] . microtime() . $rcmail->config->get('des_key'));
+
$src = $rcmail->config->get('owncloud_url');
- $user = $_SESSION['kolab_uid']; // requires kolab_auth plugin
- $pass = $rcmail->decrypt($_SESSION['password']);
+ $src .= '?kolab_auth=' . strrev(rtrim(base64_encode(http_build_query(array(
+ 'session' => session_id(),
+ 'cname' => session_name(),
+ 'token' => $_SESSION['owncloudauth'],
+ ))), '='));
+
+ return html::tag('iframe', array('id' => 'owncloudframe', 'src' => $src,
+ 'width' => "100%", 'height' => "100%", 'frameborder' => 0));
+ }
- $src = preg_replace('/^(https?:\/\/)/',
- '\\1' . urlencode($user) . ':' . urlencode($pass) . '@', $src);
+ function logout()
+ {
+ $rcmail = rcube::get_instance();
+ $this->load_config();
+
+ // send logout request to owncloud
+ $logout_url = $rcmail->config->get('owncloud_url') . '?logout=true';
+ $rcmail->output->add_script("new Image().src = '$logout_url';", 'foot');
+ }
+
+ function sso_request()
+ {
+ $response = array();
+ $sign_valid = false;
+
+ $rcmail = rcube::get_instance();
+ $this->load_config();
+
+ // check signature
+ if ($hmac = $_POST['hmac']) {
+ unset($_POST['hmac']);
+ $postdata = http_build_query($_POST, '', '&');
+ $sign_valid = ($hmac == hash_hmac('sha256', $postdata, $rcmail->config->get('owncloud_secret', '<undefined-secret>')));
+ }
+
+ // if ownCloud sent a valid auth request, return plain username and password
+ if ($sign_valid && !empty($_POST['token']) && $_POST['token'] == $_SESSION['owncloudauth']) {
+ $user = $_SESSION['kolab_uid']; // requires kolab_auth plugin
+ $pass = $rcmail->decrypt($_SESSION['password']);
+ $response = array('user' => $user, 'pass' => $pass);
+ }
- return '<iframe id="owncloudframe" width="100%" height="100%" frameborder="0"'
- .' src="' . $src. '"></iframe>';
+ echo json_encode($response);
+ exit;
}
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Thu, Jul 30, 6:09 AM (18 h, 53 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1179919
Default Alt Text
(18 KB)
Attached To
Mode
R14 roundcubemail-plugins-kolab
Attached
Detach File
Event Timeline
Log In to Comment