Skip to content

Commit

Permalink
allow admin to enforce password on mail shares
Browse files Browse the repository at this point in the history
Signed-off-by: Bjoern Schiessle <bjoern@schiessle.org>
  • Loading branch information
schiessle committed Apr 11, 2017
1 parent 1858039 commit b11610b
Show file tree
Hide file tree
Showing 15 changed files with 315 additions and 28 deletions.
3 changes: 2 additions & 1 deletion apps/sharebymail/appinfo/app.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
*
*/

$settings = new \OCA\ShareByMail\Settings();
$settings = new \OCA\ShareByMail\Settings(new \OCA\ShareByMail\Settings\SettingsManager(\OC::$server->getConfig()));

\OCP\Util::connectHook('\OCP\Config', 'js', $settings, 'announceShareProvider');
\OCP\Util::connectHook('\OCP\Config', 'js', $settings, 'announceShareByMailSettings');
3 changes: 3 additions & 0 deletions apps/sharebymail/css/settings-admin.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#ncShareByMailSettings p {
padding-top: 10px;
}
10 changes: 9 additions & 1 deletion apps/sharebymail/js/settings-admin.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,15 @@ $(function() {
if ($(this).is(':checked')) {
status = 'yes';
}
OC.AppConfig.setValue('sharebymail', 'sendpasswordmail', status);
OCP.AppConfig.setValue('sharebymail', 'sendpasswordmail', status);
});

$('#enforcePasswordProtection').on('change', function() {
var status = 'no';
if ($(this).is(':checked')) {
status = 'yes';
}
OCP.AppConfig.setValue('sharebymail', 'enforcePasswordProtection', status);
});

});
15 changes: 15 additions & 0 deletions apps/sharebymail/lib/Settings.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,17 @@
namespace OCA\ShareByMail;


use OCA\ShareByMail\Settings\SettingsManager;

class Settings {

/** @var SettingsManager */
private $settingsManager;

public function __construct(SettingsManager $settingsManager) {
$this->settingsManager = $settingsManager;
}

/**
* announce that the share-by-mail share provider is enabled
*
Expand All @@ -35,4 +44,10 @@ public function announceShareProvider(array $settings) {
$array['shareByMailEnabled'] = true;
$settings['array']['oc_appconfig'] = json_encode($array);
}

public function announceShareByMailSettings(array $settings) {
$array = json_decode($settings['array']['oc_appconfig'], true);
$array['shareByMail']['enforcePasswordProtection'] = $this->settingsManager->enforcePasswordProtection();
$settings['array']['oc_appconfig'] = json_encode($array);
}
}
3 changes: 2 additions & 1 deletion apps/sharebymail/lib/Settings/Admin.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ public function __construct(SettingsManager $settingsManager) {
public function getForm() {

$parameters = [
'sendPasswordMail' => $this->settingsManager->sendPasswordByMail()
'sendPasswordMail' => $this->settingsManager->sendPasswordByMail(),
'enforcePasswordProtection' => $this->settingsManager->enforcePasswordProtection()
];

return new TemplateResponse('sharebymail', 'settings-admin', $parameters, '');
Expand Down
16 changes: 14 additions & 2 deletions apps/sharebymail/lib/Settings/SettingsManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ class SettingsManager {
/** @var IConfig */
private $config;

private $defaultSetting = 'yes';
private $sendPasswordByMailDefault = 'yes';

private $enforcePasswordProtectionDefault = 'no';

public function __construct(IConfig $config) {
$this->config = $config;
Expand All @@ -42,8 +44,18 @@ public function __construct(IConfig $config) {
* @return bool
*/
public function sendPasswordByMail() {
$sendPasswordByMail = $this->config->getAppValue('sharebymail', 'sendpasswordmail', $this->defaultSetting);
$sendPasswordByMail = $this->config->getAppValue('sharebymail', 'sendpasswordmail', $this->sendPasswordByMailDefault);
return $sendPasswordByMail === 'yes';
}

/**
* do we require a share by mail to be password protected
*
* @return bool
*/
public function enforcePasswordProtection() {
$enforcePassword = $this->config->getAppValue('sharebymail', 'enforcePasswordProtection', $this->enforcePasswordProtectionDefault);
return $enforcePassword === 'yes';
}

}
128 changes: 114 additions & 14 deletions apps/sharebymail/lib/ShareByMailProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
use OCP\IUser;
use OCP\IUserManager;
use OCP\Mail\IMailer;
use OCP\Security\IHasher;
use OCP\Security\ISecureRandom;
use OC\Share20\Share;
use OCP\Share\Exceptions\ShareNotFound;
Expand Down Expand Up @@ -80,6 +81,9 @@ class ShareByMailProvider implements IShareProvider {
/** @var SettingsManager */
private $settingsManager;

/** @var IHasher */
private $hasher;

/**
* Return the identifier of this provider.
*
Expand All @@ -102,6 +106,7 @@ public function identifier() {
* @param IURLGenerator $urlGenerator
* @param IManager $activityManager
* @param SettingsManager $settingsManager
* @param IHasher $hasher
*/
public function __construct(
IDBConnection $connection,
Expand All @@ -113,7 +118,8 @@ public function __construct(
IMailer $mailer,
IURLGenerator $urlGenerator,
IManager $activityManager,
SettingsManager $settingsManager
SettingsManager $settingsManager,
IHasher $hasher
) {
$this->dbConnection = $connection;
$this->secureRandom = $secureRandom;
Expand All @@ -125,6 +131,7 @@ public function __construct(
$this->urlGenerator = $urlGenerator;
$this->activityManager = $activityManager;
$this->settingsManager = $settingsManager;
$this->hasher = $hasher;
}

/**
Expand All @@ -149,13 +156,50 @@ public function create(IShare $share) {
throw new \Exception($message_t);
}

// if the admin enforces a password for all mail shares we create a
// random password and send it to the recipient
$password = '';
$passwordEnforced = $this->settingsManager->enforcePasswordProtection();
$allowPasswordByMail = $this->settingsManager->sendPasswordByMail();
if ($passwordEnforced) {
$password = $this->generateToken(8);
$share->setPassword($this->hasher->hash($password));
$initiatorUser = $this->userManager->get($share->getSharedBy());
$initiatorEMailAddress = ($initiatorUser instanceof IUser) ? $initiatorUser->getEMailAddress() : null;
}

if ($passwordEnforced && $initiatorEMailAddress === null && !$allowPasswordByMail) {
throw new \Exception(
$this->l->t("We can't send you the auto-generated password. Please set a valid email address in your personal settings and try again.")
);
}

$shareId = $this->createMailShare($share);
$send = $this->sendPassword($share->getNode()->getName(), $share->getSharedBy(), $share->getSharedWith(), $password);
if ($passwordEnforced && $send === false) {
$this->sendPasswordToOwner($share->getNode()->getName(), $share->getSharedBy(), $shareWith, $password);
}
$this->createActivity($share);
$data = $this->getRawShare($shareId);

return $this->createShareObject($data);

}

/**
* auto generate password in case of password enforcement on mail shares
*
* @return string
*/
protected function autoGeneratePassword() {
$password = '';
if ($this->settingsManager->enforcePasswordProtection()) {
$password = $this->generateToken(8);
}

return $password;
}

/**
* create activity if a file/folder was shared by mail
*
Expand Down Expand Up @@ -223,7 +267,8 @@ protected function createMailShare(IShare $share) {
$share->getSharedBy(),
$share->getShareOwner(),
$share->getPermissions(),
$share->getToken()
$share->getToken(),
$share->getPassword()
);

try {
Expand Down Expand Up @@ -253,6 +298,7 @@ protected function sendMailNotification($filename, $link, $owner, $initiator, $s
$initiatorUser = $this->userManager->get($initiator);
$ownerDisplayName = ($ownerUser instanceof IUser) ? $ownerUser->getDisplayName() : $owner;
$initiatorDisplayName = ($initiatorUser instanceof IUser) ? $initiatorUser->getDisplayName() : $initiator;
$initiatorEmailAddress = ($initiatorUser instanceof IUser) ? $initiatorUser->getEMailAddress() : null;
if ($owner === $initiator) {
$subject = (string)$this->l->t('%s shared »%s« with you', array($ownerDisplayName, $filename));
} else {
Expand All @@ -263,6 +309,9 @@ protected function sendMailNotification($filename, $link, $owner, $initiator, $s
$htmlBody = $this->createMailBody('mail', $filename, $link, $ownerDisplayName, $initiatorDisplayName);
$textBody = $this->createMailBody('altmail', $filename, $link, $ownerDisplayName, $initiatorDisplayName);
$message->setTo([$shareWith]);
if ($initiatorEmailAddress) {
$message->setFrom([$initiatorEmailAddress]);
}
$message->setSubject($subject);
$message->setBody($textBody, 'text/plain');
$message->setHtmlBody($htmlBody);
Expand Down Expand Up @@ -304,43 +353,93 @@ protected function createMailBody($template, $filename, $link, $owner, $initiato
* @param string $filename
* @param string $initiator
* @param string $shareWith
* @param string $password
* @return bool
*/
protected function sendPassword($filename, $initiator, $shareWith, $password) {

if ($this->settingsManager->sendPasswordByMail() === false) {
return;
if ($password === '' || $this->settingsManager->sendPasswordByMail() === false) {
return false;
}

$initiatorUser = $this->userManager->get($initiator);
$initiatorDisplayName = ($initiatorUser instanceof IUser) ? $initiatorUser->getDisplayName() : $initiator;
$initiatorEmailAddress = ($initiatorUser instanceof IUser) ? $initiatorUser->getEMailAddress() : null;
$subject = (string)$this->l->t('Password to access »%s« shared to you by %s', [$filename, $initiatorDisplayName]);

$message = $this->mailer->createMessage();
$htmlBody = $this->createMailBodyToSendPassword('mailpassword', $filename, $initiatorDisplayName, $password);
$textBody = $this->createMailBodyToSendPassword('altmailpassword', $filename,$initiatorDisplayName, $password);
$arguments = ['filename' => $filename, 'initiator' => $initiator, 'password' => $password];
$htmlBody = $this->createMailBodyToSendPassword('mailpassword', $arguments);
$textBody = $this->createMailBodyToSendPassword('altmailpassword', $arguments);
$message->setTo([$shareWith]);
if ($initiatorEmailAddress) {
$message->setFrom([$initiatorEmailAddress]);
}
$message->setSubject($subject);
$message->setBody($textBody, 'text/plain');
$message->setHtmlBody($htmlBody);
$this->mailer->send($message);

return true;
}

/**
* create mail body to send password to recipient
* send auto generated password to the owner. This happens if the admin enforces
* a password for mail shares and forbid to send the password by mail to the recipient
*
* @param string $filename
* @param string $initiator
* @param string $shareWith
* @param string $password
* @throws \Exception
*/
protected function sendPasswordToOwner($filename, $initiator, $shareWith, $password) {

$initiatorUser = $this->userManager->get($initiator);
$initiatorDisplayName = ($initiatorUser instanceof IUser) ? $initiatorUser->getDisplayName() : $initiator;
$initiatorEMailAddress = ($initiatorUser instanceof IUser) ? $initiatorUser->getEMailAddress() : null;
$initiatorFullMail = $initiatorDisplayName . '<' . $initiatorEMailAddress . '>';

if ($initiatorEMailAddress === null) {
throw new \Exception(
$this->l->t("We can't send you the auto-generated password. Please set a valid email address in your personal settings and try again.")
);
}

$subject = (string)$this->l->t('Password to access »%s« shared with %s', [$filename, $shareWith]);

$message = $this->mailer->createMessage();
$arguments = ['filename' => $filename, 'recipient' => $shareWith, 'password' => $password];
$htmlBody = $this->createMailBodyToSendPassword('mailpasswordowner', $arguments);
$textBody = $this->createMailBodyToSendPassword('altmailpasswordowner', $arguments);
if ($initiatorEMailAddress) {
$message->setFrom([$initiatorUser->getEMailAddress()]);
}
$message->setTo([$initiatorEMailAddress]);
$message->setFrom([$initiatorEMailAddress]);
$message->setSubject($subject);
$message->setBody($textBody, 'text/plain');
$message->setHtmlBody($htmlBody);
$this->mailer->send($message);

}

/**
* create mail body to send password to recipient
*
* @param string $template
* @param array $arguments
* @return string plain text mail
* @throws HintException
*/
protected function createMailBodyToSendPassword($template, $filename, $initiator, $password) {
protected function createMailBodyToSendPassword($template, array $arguments) {

$mailBodyTemplate = new Template('sharebymail', $template, '');
$mailBodyTemplate->assign ('filename', \OCP\Util::sanitizeHTML($filename));
$mailBodyTemplate->assign ('password', \OCP\Util::sanitizeHTML($password));
$mailBodyTemplate->assign ('initiator', \OCP\Util::sanitizeHTML($initiator));

foreach ($arguments as $key => $value) {
$mailBodyTemplate->assign ($key, \OCP\Util::sanitizeHTML($value));
}

$mailBody = $mailBodyTemplate->fetchPage();

if (is_string($mailBody)) {
Expand All @@ -357,9 +456,9 @@ protected function createMailBodyToSendPassword($template, $filename, $initiator
*
* @return string
*/
protected function generateToken() {
protected function generateToken($size = 15) {
$token = $this->secureRandom->generate(
15, ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_UPPER . ISecureRandom::CHAR_DIGITS);
$size, ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_UPPER . ISecureRandom::CHAR_DIGITS);
return $token;
}

Expand Down Expand Up @@ -400,7 +499,7 @@ public function getChildren(IShare $parent) {
* @param string $token
* @return int
*/
protected function addShareToDB($itemSource, $itemType, $shareWith, $sharedBy, $uidOwner, $permissions, $token) {
protected function addShareToDB($itemSource, $itemType, $shareWith, $sharedBy, $uidOwner, $permissions, $token, $password) {
$qb = $this->dbConnection->getQueryBuilder();
$qb->insert('share')
->setValue('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL))
Expand All @@ -412,6 +511,7 @@ protected function addShareToDB($itemSource, $itemType, $shareWith, $sharedBy, $
->setValue('uid_initiator', $qb->createNamedParameter($sharedBy))
->setValue('permissions', $qb->createNamedParameter($permissions))
->setValue('token', $qb->createNamedParameter($token))
->setValue('password', $qb->createNamedParameter($password))
->setValue('stime', $qb->createNamedParameter(time()));

/*
Expand Down
32 changes: 32 additions & 0 deletions apps/sharebymail/templates/altmailpasswordowner.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php
/**
* @copyright Copyright (c) 2016 Bjoern Schiessle <bjoern@schiessle.org>
*
* @license GNU AGPL version 3 or any later version
*
* This program 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.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*
*/

/** @var OC_Theme $theme */
/** @var array $_ */
print_unescaped($l->t("Hey there,\n\nyou just shared »%s« with %s.\nThe share was already send to the recipient. Due to the security policies\neach share needs to be protected by email and it is not allowed to send the\npassword directly by mail to the recipient. Therefore you need to forward\nthe password manually to the recipient.\n\nThis is the password: %s\n\nYou can chose a different password at any time in the share dialog.\n\n", [$_['filename'], $_['recipient'], $_['password']]));
// TRANSLATORS term at the end of a mail
p($l->t("Cheers!"));
print_unescaped("\n");
?>

--
<?php p($theme->getName() . ' - ' . $theme->getSlogan()); ?>
<?php print_unescaped("\n".$theme->getBaseUrl());
Loading

0 comments on commit b11610b

Please sign in to comment.