Allow to disable password policy enforcement for selected groupstags/v24.0.0beta3
@@ -139,6 +139,7 @@ class CapabilitiesTest extends \Test\TestCase { | |||
$map = [ | |||
['core', 'shareapi_enabled', 'yes', 'yes'], | |||
['core', 'shareapi_allow_links', 'yes', 'yes'], | |||
['core', 'shareapi_enforce_links_password_excluded_groups', '', ''], | |||
]; | |||
$result = $this->getResults($map); | |||
$this->assertIsArray($result['public']); | |||
@@ -149,6 +150,7 @@ class CapabilitiesTest extends \Test\TestCase { | |||
$map = [ | |||
['core', 'shareapi_enabled', 'yes', 'yes'], | |||
['core', 'shareapi_allow_links', 'yes', 'yes'], | |||
['core', 'shareapi_enforce_links_password_excluded_groups', '', ''], | |||
['core', 'shareapi_enforce_links_password', 'no', 'yes'], | |||
]; | |||
$result = $this->getResults($map); | |||
@@ -161,6 +163,7 @@ class CapabilitiesTest extends \Test\TestCase { | |||
$map = [ | |||
['core', 'shareapi_enabled', 'yes', 'yes'], | |||
['core', 'shareapi_allow_links', 'yes', 'yes'], | |||
['core', 'shareapi_enforce_links_password_excluded_groups', '', ''], | |||
['core', 'shareapi_enforce_links_password', 'no', 'no'], | |||
]; | |||
$result = $this->getResults($map); | |||
@@ -174,6 +177,7 @@ class CapabilitiesTest extends \Test\TestCase { | |||
['core', 'shareapi_enabled', 'yes', 'yes'], | |||
['core', 'shareapi_allow_links', 'yes', 'yes'], | |||
['core', 'shareapi_default_expire_date', 'no', 'no'], | |||
['core', 'shareapi_enforce_links_password_excluded_groups', '', ''], | |||
]; | |||
$result = $this->getResults($map); | |||
$this->assertArrayHasKey('expire_date', $result['public']); | |||
@@ -188,6 +192,7 @@ class CapabilitiesTest extends \Test\TestCase { | |||
['core', 'shareapi_default_expire_date', 'no', 'yes'], | |||
['core', 'shareapi_expire_after_n_days', '7', '7'], | |||
['core', 'shareapi_enforce_expire_date', 'no', 'no'], | |||
['core', 'shareapi_enforce_links_password_excluded_groups', '', ''], | |||
]; | |||
$result = $this->getResults($map); | |||
$this->assertArrayHasKey('expire_date', $result['public']); | |||
@@ -203,6 +208,7 @@ class CapabilitiesTest extends \Test\TestCase { | |||
['core', 'shareapi_allow_links', 'yes', 'yes'], | |||
['core', 'shareapi_default_expire_date', 'no', 'yes'], | |||
['core', 'shareapi_enforce_expire_date', 'no', 'yes'], | |||
['core', 'shareapi_enforce_links_password_excluded_groups', '', ''], | |||
]; | |||
$result = $this->getResults($map); | |||
$this->assertArrayHasKey('expire_date', $result['public']); | |||
@@ -215,6 +221,7 @@ class CapabilitiesTest extends \Test\TestCase { | |||
['core', 'shareapi_enabled', 'yes', 'yes'], | |||
['core', 'shareapi_allow_links', 'yes', 'yes'], | |||
['core', 'shareapi_allow_public_notification', 'no', 'yes'], | |||
['core', 'shareapi_enforce_links_password_excluded_groups', '', ''], | |||
]; | |||
$result = $this->getResults($map); | |||
$this->assertTrue($result['public']['send_mail']); | |||
@@ -225,6 +232,7 @@ class CapabilitiesTest extends \Test\TestCase { | |||
['core', 'shareapi_enabled', 'yes', 'yes'], | |||
['core', 'shareapi_allow_links', 'yes', 'yes'], | |||
['core', 'shareapi_allow_public_notification', 'no', 'no'], | |||
['core', 'shareapi_enforce_links_password_excluded_groups', '', ''], | |||
]; | |||
$result = $this->getResults($map); | |||
$this->assertFalse($result['public']['send_mail']); | |||
@@ -234,6 +242,7 @@ class CapabilitiesTest extends \Test\TestCase { | |||
$map = [ | |||
['core', 'shareapi_enabled', 'yes', 'yes'], | |||
['core', 'shareapi_allow_resharing', 'yes', 'yes'], | |||
['core', 'shareapi_enforce_links_password_excluded_groups', '', ''], | |||
]; | |||
$result = $this->getResults($map); | |||
$this->assertTrue($result['resharing']); | |||
@@ -243,6 +252,7 @@ class CapabilitiesTest extends \Test\TestCase { | |||
$map = [ | |||
['core', 'shareapi_enabled', 'yes', 'yes'], | |||
['core', 'shareapi_allow_resharing', 'yes', 'no'], | |||
['core', 'shareapi_enforce_links_password_excluded_groups', '', ''], | |||
]; | |||
$result = $this->getResults($map); | |||
$this->assertFalse($result['resharing']); | |||
@@ -253,6 +263,7 @@ class CapabilitiesTest extends \Test\TestCase { | |||
['core', 'shareapi_enabled', 'yes', 'yes'], | |||
['core', 'shareapi_allow_links', 'yes', 'yes'], | |||
['core', 'shareapi_allow_public_upload', 'yes', 'yes'], | |||
['core', 'shareapi_enforce_links_password_excluded_groups', '', ''], | |||
]; | |||
$result = $this->getResults($map); | |||
$this->assertTrue($result['public']['upload']); | |||
@@ -264,6 +275,7 @@ class CapabilitiesTest extends \Test\TestCase { | |||
['core', 'shareapi_enabled', 'yes', 'yes'], | |||
['core', 'shareapi_allow_links', 'yes', 'yes'], | |||
['core', 'shareapi_allow_public_upload', 'yes', 'no'], | |||
['core', 'shareapi_enforce_links_password_excluded_groups', '', ''], | |||
]; | |||
$result = $this->getResults($map); | |||
$this->assertFalse($result['public']['upload']); |
@@ -72,6 +72,11 @@ class Sharing implements IDelegatedSettings { | |||
$linksExcludeGroupsList = !is_null(json_decode($linksExcludedGroups)) | |||
? implode('|', json_decode($linksExcludedGroups, true)) : ''; | |||
$excludedPasswordGroups = $this->config->getAppValue('core', 'shareapi_enforce_links_password_excluded_groups', ''); | |||
$excludedPasswordGroupsList = !is_null(json_decode($excludedPasswordGroups)) | |||
? implode('|', json_decode($excludedPasswordGroups, true)) : ''; | |||
$parameters = [ | |||
// Built-In Sharing | |||
'sharingAppEnabled' => $this->appManager->isEnabledForUser('files_sharing'), | |||
@@ -84,7 +89,9 @@ class Sharing implements IDelegatedSettings { | |||
'restrictUserEnumerationToGroup' => $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_group', 'no'), | |||
'restrictUserEnumerationToPhone' => $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_phone', 'no'), | |||
'restrictUserEnumerationFullMatch' => $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_full_match', 'yes'), | |||
'enforceLinkPassword' => Util::isPublicLinkPasswordRequired(), | |||
'enforceLinkPassword' => Util::isPublicLinkPasswordRequired(false), | |||
'passwordExcludedGroups' => $excludedPasswordGroupsList, | |||
'passwordExcludedGroupsFeatureEnabled' => $this->config->getSystemValueBool('sharing.allow_disabled_password_enforcement_groups', false), | |||
'onlyShareWithGroupMembers' => $this->shareManager->shareWithGroupMembersOnly(), | |||
'shareAPIEnabled' => $this->config->getAppValue('core', 'shareapi_enabled', 'yes'), | |||
'shareDefaultExpireDateSet' => $this->config->getAppValue('core', 'shareapi_default_expire_date', 'no'), |
@@ -1,5 +1,5 @@ | |||
window.addEventListener('DOMContentLoaded', () => { | |||
$('#excludedGroups,#linksExcludedGroups').each((index, element) => { | |||
$('#excludedGroups,#linksExcludedGroups,#passwordsExcludedGroups').each(function(index, element) { | |||
OC.Settings.setupGroupsSelect($(element)) | |||
$(element).change((ev) => { | |||
let groups = ev.val || [] | |||
@@ -93,6 +93,10 @@ window.addEventListener('DOMContentLoaded', () => { | |||
$('#setDefaultRemoteExpireDate').toggleClass('hidden', !this.checked) | |||
}) | |||
$('#enforceLinkPassword').change(function() { | |||
$('#selectPasswordsExcludedGroups').toggleClass('hidden', !this.checked) | |||
}) | |||
$('#publicShareDisclaimer').change(function() { | |||
$('#publicShareDisclaimerText').toggleClass('hidden', !this.checked) | |||
if (!this.checked) { |
@@ -120,6 +120,18 @@ | |||
} ?> /> | |||
<label for="enforceLinkPassword"><?php p($l->t('Enforce password protection'));?></label><br/> | |||
<?php if ($_['passwordExcludedGroupsFeatureEnabled']) { ?> | |||
<div id="selectPasswordsExcludedGroups" class="indent <?php if (!$_['enforceLinkPassword']) { p('hidden'); } ?>"> | |||
<div class="indent"> | |||
<label for="shareapi_enforce_links_password_excluded_groups"><?php p($l->t('Exclude groups from password requirements:'));?> | |||
<br /> | |||
<input name="shareapi_enforce_links_password_excluded_groups" id="passwordsExcludedGroups" value="<?php p($_['passwordExcludedGroups']) ?>" style="width: 400px" class="noJSAutoUpdate"/> | |||
</div> | |||
</div> | |||
<?php } ?> | |||
<input type="checkbox" name="shareapi_default_expire_date" id="shareapiDefaultExpireDate" class="checkbox" value="1" <?php if ($_['shareDefaultExpireDateSet'] === 'yes') { print_unescaped('checked="checked"'); } ?> /> | |||
<input type="checkbox" name="shareapi_default_expire_date" id="shareapiDefaultExpireDate" class="checkbox" | |||
value="1" <?php if ($_['shareDefaultExpireDateSet'] === 'yes') { | |||
print_unescaped('checked="checked"'); |
@@ -135,6 +135,8 @@ class SharingTest extends TestCase { | |||
'shareRemoteExpireAfterNDays' => '7', | |||
'shareRemoteEnforceExpireDate' => 'no', | |||
'allowLinksExcludeGroups' => '', | |||
'passwordExcludedGroups' => '', | |||
'passwordExcludedGroupsFeatureEnabled' => false, | |||
], | |||
'' | |||
); | |||
@@ -208,6 +210,8 @@ class SharingTest extends TestCase { | |||
'shareRemoteExpireAfterNDays' => '7', | |||
'shareRemoteEnforceExpireDate' => 'no', | |||
'allowLinksExcludeGroups' => '', | |||
'passwordExcludedGroups' => '', | |||
'passwordExcludedGroupsFeatureEnabled' => false, | |||
], | |||
'' | |||
); |
@@ -1580,6 +1580,11 @@ $CONFIG = [ | |||
*/ | |||
'sharing.enable_share_mail' => true, | |||
/** | |||
* Set to true to enable the feature to add exceptions for share password enforcement | |||
*/ | |||
'sharing.allow_disabled_password_enforcement_groups' => false, | |||
/** | |||
* Set to true to always transfer incoming shares by default | |||
* when running "occ files:transfer-ownership". |
@@ -1783,9 +1783,21 @@ class Manager implements IManager { | |||
/** | |||
* Is password on public link requires | |||
* | |||
* @param bool Check group membership exclusion | |||
* @return bool | |||
*/ | |||
public function shareApiLinkEnforcePassword() { | |||
public function shareApiLinkEnforcePassword(bool $checkGroupMembership = true) { | |||
$excludedGroups = $this->config->getAppValue('core', 'shareapi_enforce_links_password_excluded_groups', ''); | |||
if ($excludedGroups !== '' && $checkGroupMembership) { | |||
$excludedGroups = json_decode($excludedGroups); | |||
$user = $this->userSession->getUser(); | |||
if ($user) { | |||
$userGroups = $this->groupManager->getUserGroupIds($user); | |||
if ((bool)array_intersect($excludedGroups, $userGroups)) { | |||
return false; | |||
} | |||
} | |||
} | |||
return $this->config->getAppValue('core', 'shareapi_enforce_links_password', 'no') === 'yes'; | |||
} | |||
@@ -116,15 +116,16 @@ class OC_Util { | |||
} | |||
/** | |||
* check if a password is required for each public link | |||
* Check if a password is required for each public link | |||
* | |||
* @param bool $checkGroupMembership Check group membership exclusion | |||
* @return boolean | |||
* @suppress PhanDeprecatedFunction | |||
*/ | |||
public static function isPublicLinkPasswordRequired() { | |||
public static function isPublicLinkPasswordRequired(bool $checkGroupMembership = true) { | |||
/** @var IManager $shareManager */ | |||
$shareManager = \OC::$server->get(IManager::class); | |||
return $shareManager->shareApiLinkEnforcePassword(); | |||
return $shareManager->shareApiLinkEnforcePassword($checkGroupMembership); | |||
} | |||
/** |
@@ -321,10 +321,12 @@ interface IManager { | |||
/** | |||
* Is password on public link requires | |||
* | |||
* @param bool $checkGroupMembership Check group membership exclusion | |||
* @return bool | |||
* @since 9.0.0 | |||
* @since 24.0.0 Added optional $checkGroupMembership parameter | |||
*/ | |||
public function shareApiLinkEnforcePassword(); | |||
public function shareApiLinkEnforcePassword(bool $checkGroupMembership = true); | |||
/** | |||
* Is default expire date enabled |
@@ -547,12 +547,14 @@ class Util { | |||
} | |||
/** | |||
* check if a password is required for each public link | |||
* Check if a password is required for each public link | |||
* | |||
* @param bool $checkGroupMembership Check group membership exclusion | |||
* @return boolean | |||
* @since 7.0.0 | |||
*/ | |||
public static function isPublicLinkPasswordRequired() { | |||
return \OC_Util::isPublicLinkPasswordRequired(); | |||
public static function isPublicLinkPasswordRequired(bool $checkGroupMembership = true) { | |||
return \OC_Util::isPublicLinkPasswordRequired($checkGroupMembership); | |||
} | |||
/** |
@@ -505,14 +505,46 @@ class ManagerTest extends \Test\TestCase { | |||
$this->expectExceptionMessage('Passwords are enforced for link and mail shares'); | |||
$this->config->method('getAppValue')->willReturnMap([ | |||
['core', 'shareapi_enforce_links_password_excluded_groups', '', ''], | |||
['core', 'shareapi_enforce_links_password', 'no', 'yes'], | |||
]); | |||
self::invokePrivate($this->manager, 'verifyPassword', [null]); | |||
} | |||
public function testVerifyPasswordNotEnforcedGroup() { | |||
$this->config->method('getAppValue')->willReturnMap([ | |||
['core', 'shareapi_enforce_links_password_excluded_groups', '', '["admin"]'], | |||
['core', 'shareapi_enforce_links_password', 'no', 'yes'], | |||
]); | |||
// Create admin user | |||
$user = $this->createMock(IUser::class); | |||
$this->userSession->method('getUser')->willReturn($user); | |||
$this->groupManager->method('getUserGroupIds')->with($user)->willReturn(['admin']); | |||
$result = self::invokePrivate($this->manager, 'verifyPassword', [null]); | |||
$this->assertNull($result); | |||
} | |||
public function testVerifyPasswordNotEnforcedMultipleGroups() { | |||
$this->config->method('getAppValue')->willReturnMap([ | |||
['core', 'shareapi_enforce_links_password_excluded_groups', '', '["admin", "special"]'], | |||
['core', 'shareapi_enforce_links_password', 'no', 'yes'], | |||
]); | |||
// Create admin user | |||
$user = $this->createMock(IUser::class); | |||
$this->userSession->method('getUser')->willReturn($user); | |||
$this->groupManager->method('getUserGroupIds')->with($user)->willReturn(['special']); | |||
$result = self::invokePrivate($this->manager, 'verifyPassword', [null]); | |||
$this->assertNull($result); | |||
} | |||
public function testVerifyPasswordNull() { | |||
$this->config->method('getAppValue')->willReturnMap([ | |||
['core', 'shareapi_enforce_links_password_excluded_groups', '', ''], | |||
['core', 'shareapi_enforce_links_password', 'no', 'no'], | |||
]); | |||
@@ -522,6 +554,7 @@ class ManagerTest extends \Test\TestCase { | |||
public function testVerifyPasswordHook() { | |||
$this->config->method('getAppValue')->willReturnMap([ | |||
['core', 'shareapi_enforce_links_password_excluded_groups', '', ''], | |||
['core', 'shareapi_enforce_links_password', 'no', 'no'], | |||
]); | |||
@@ -543,6 +576,7 @@ class ManagerTest extends \Test\TestCase { | |||
$this->expectExceptionMessage('password not accepted'); | |||
$this->config->method('getAppValue')->willReturnMap([ | |||
['core', 'shareapi_enforce_links_password_excluded_groups', '', ''], | |||
['core', 'shareapi_enforce_links_password', 'no', 'no'], | |||
]); | |||