Skip to content

Commit

Permalink
Allow to disable password policy enforcement for selected groups
Browse files Browse the repository at this point in the history
Signed-off-by: Carl Schwan <carl@carlschwan.eu>
Co-authored-by: Vincent Petry <vincent@nextcloud.com>
Signed-off-by: nextcloud-command <nextcloud-command@users.noreply.github.com>
  • Loading branch information
2 people authored and nextcloud-command committed Mar 28, 2022
1 parent a99fdf5 commit 9430927
Show file tree
Hide file tree
Showing 13 changed files with 105 additions and 13 deletions.
12 changes: 12 additions & 0 deletions apps/files_sharing/tests/CapabilitiesTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ public function testOnlyLinkSharing() {
$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']);
Expand All @@ -149,6 +150,7 @@ public function testLinkPassword() {
$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);
Expand All @@ -161,6 +163,7 @@ public function testLinkNoPassword() {
$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);
Expand All @@ -174,6 +177,7 @@ public function testLinkNoExpireDate() {
['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']);
Expand All @@ -188,6 +192,7 @@ public function testLinkExpireDate() {
['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']);
Expand All @@ -203,6 +208,7 @@ public function testLinkExpireDateEnforced() {
['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']);
Expand All @@ -215,6 +221,7 @@ public function testLinkSendMail() {
['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']);
Expand All @@ -225,6 +232,7 @@ public function testLinkNoSendMail() {
['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']);
Expand All @@ -234,6 +242,7 @@ public function testResharing() {
$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']);
Expand All @@ -243,6 +252,7 @@ public function testNoResharing() {
$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']);
Expand All @@ -253,6 +263,7 @@ public function testLinkPublicUpload() {
['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']);
Expand All @@ -264,6 +275,7 @@ public function testLinkNoPublicUpload() {
['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']);
Expand Down
9 changes: 8 additions & 1 deletion apps/settings/lib/Settings/Admin/Sharing.php
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,11 @@ public function getForm() {
$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'),
Expand All @@ -84,7 +89,9 @@ public function getForm() {
'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'),
Expand Down
6 changes: 5 additions & 1 deletion apps/settings/src/admin.js
Original file line number Diff line number Diff line change
@@ -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 || []
Expand Down Expand Up @@ -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) {
Expand Down
12 changes: 12 additions & 0 deletions apps/settings/templates/settings/admin/sharing.php
Original file line number Diff line number Diff line change
Expand Up @@ -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"');
Expand Down
4 changes: 4 additions & 0 deletions apps/settings/tests/Settings/Admin/SharingTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,8 @@ public function testGetFormWithoutExcludedGroups(): void {
'shareRemoteExpireAfterNDays' => '7',
'shareRemoteEnforceExpireDate' => 'no',
'allowLinksExcludeGroups' => '',
'passwordExcludedGroups' => '',
'passwordExcludedGroupsFeatureEnabled' => false,
],
''
);
Expand Down Expand Up @@ -208,6 +210,8 @@ public function testGetFormWithExcludedGroups(): void {
'shareRemoteExpireAfterNDays' => '7',
'shareRemoteEnforceExpireDate' => 'no',
'allowLinksExcludeGroups' => '',
'passwordExcludedGroups' => '',
'passwordExcludedGroupsFeatureEnabled' => false,
],
''
);
Expand Down
5 changes: 5 additions & 0 deletions config/config.sample.php
Original file line number Diff line number Diff line change
Expand Up @@ -1572,6 +1572,11 @@
*/
'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".
Expand Down
4 changes: 2 additions & 2 deletions dist/settings-legacy-admin.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/settings-legacy-admin.js.map

Large diffs are not rendered by default.

11 changes: 10 additions & 1 deletion lib/private/Share20/Manager.php
Original file line number Diff line number Diff line change
Expand Up @@ -1784,9 +1784,18 @@ public function shareApiAllowLinks() {
/**
* Is password on public link requires
*
* @param bool Check group membership exclusion
* @return bool
*/
public function shareApiLinkEnforcePassword() {
public function shareApiLinkEnforcePassword(bool $checkGroupMembership = true) {
$user = $this->userSession->getUser();
if ($checkGroupMembership && $user !== null) {
$userGroups = $this->groupManager->getUserGroupIds($user);
$excludedGroups = json_decode($this->config->getAppValue('core', 'shareapi_enforce_links_password_excluded_groups', '[]'));
if (count(array_intersect($excludedGroups, $userGroups)) > 0) {
return false;
}
}
return $this->config->getAppValue('core', 'shareapi_enforce_links_password', 'no') === 'yes';
}

Expand Down
7 changes: 4 additions & 3 deletions lib/private/legacy/OC_Util.php
Original file line number Diff line number Diff line change
Expand Up @@ -116,15 +116,16 @@ public static function setupFS(?string $user = '') {
}

/**
* 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);
}

/**
Expand Down
4 changes: 3 additions & 1 deletion lib/public/Share/IManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -321,10 +321,12 @@ public function shareApiAllowLinks();
/**
* 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
Expand Down
8 changes: 5 additions & 3 deletions lib/public/Util.php
Original file line number Diff line number Diff line change
Expand Up @@ -547,12 +547,14 @@ public static function naturalSortCompare($a, $b) {
}

/**
* 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);
}

/**
Expand Down
34 changes: 34 additions & 0 deletions tests/lib/Share20/ManagerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -505,14 +505,46 @@ public function testVerifyPasswordNullButEnforced() {
$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'],
]);

Expand All @@ -522,6 +554,7 @@ public function testVerifyPasswordNull() {

public function testVerifyPasswordHook() {
$this->config->method('getAppValue')->willReturnMap([
['core', 'shareapi_enforce_links_password_excluded_groups', '', ''],
['core', 'shareapi_enforce_links_password', 'no', 'no'],
]);

Expand All @@ -543,6 +576,7 @@ public function testVerifyPasswordHookFails() {
$this->expectExceptionMessage('password not accepted');

$this->config->method('getAppValue')->willReturnMap([
['core', 'shareapi_enforce_links_password_excluded_groups', '', ''],
['core', 'shareapi_enforce_links_password', 'no', 'no'],
]);

Expand Down

0 comments on commit 9430927

Please sign in to comment.