aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.drone.yml2
-rw-r--r--apps/federatedfilesharing/css/settings-personal.scss3
-rw-r--r--apps/federatedfilesharing/img/social-googleplus.svg1
-rw-r--r--apps/federatedfilesharing/templates/settings-personal.php4
-rw-r--r--apps/user_ldap/lib/Connection.php3
-rw-r--r--apps/user_ldap/lib/Group_LDAP.php140
-rw-r--r--apps/user_ldap/tests/Group_LDAPTest.php57
-rw-r--r--build/integration/features/bootstrap/LDAPContext.php8
-rw-r--r--build/integration/ldap_features/ldap-openldap.feature64
-rw-r--r--build/integration/ldap_features/openldap-numerical-id.feature36
-rw-r--r--lib/private/App/AppStore/Bundles/SocialSharingBundle.php1
-rw-r--r--lib/private/Files/Cache/Cache.php30
-rw-r--r--lib/private/Files/Cache/Scanner.php2
-rw-r--r--lib/private/Files/Cache/Wrapper/CacheJail.php4
-rw-r--r--lib/private/Files/Cache/Wrapper/CacheWrapper.php4
-rw-r--r--tests/lib/App/AppStore/Bundles/SocialSharingBundleTest.php1
-rw-r--r--tests/lib/Files/Cache/ScannerTest.php38
17 files changed, 300 insertions, 98 deletions
diff --git a/.drone.yml b/.drone.yml
index e3cfc2d7be5..f821333ee91 100644
--- a/.drone.yml
+++ b/.drone.yml
@@ -1049,7 +1049,7 @@ services:
matrix:
TESTS: acceptance
openldap:
- image: nextcloudci/openldap:openldap-6
+ image: nextcloudci/openldap:openldap-7
environment:
- SLAPD_DOMAIN=nextcloud.ci
- SLAPD_ORGANIZATION=Nextcloud
diff --git a/apps/federatedfilesharing/css/settings-personal.scss b/apps/federatedfilesharing/css/settings-personal.scss
index eda7a80cb1a..c64a6fc9e75 100644
--- a/apps/federatedfilesharing/css/settings-personal.scss
+++ b/apps/federatedfilesharing/css/settings-personal.scss
@@ -33,6 +33,3 @@
.social-facebook {
@include icon-color('social-facebook', 'federatedfilesharing', $color-black);
}
-.social-googleplus {
- @include icon-color('social-googleplus', 'federatedfilesharing', $color-black);
-}
diff --git a/apps/federatedfilesharing/img/social-googleplus.svg b/apps/federatedfilesharing/img/social-googleplus.svg
deleted file mode 100644
index 53af74c64f1..00000000000
--- a/apps/federatedfilesharing/img/social-googleplus.svg
+++ /dev/null
@@ -1 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" height="32" id="Layer_1" viewBox="0 0 32 32" width="32"><path d="M12.26 1c-1.044 0-2.164.12-3.36.365-1.207.284-2.37.885-3.49 1.807C3.776 4.748 2.96 6.502 2.96 8.438c0 1.6.576 3.003 1.73 4.21 1.098 1.292 2.697 1.953 4.795 1.98.395 0 .818-.028 1.266-.078-.074.207-.155.43-.24.676-.1.23-.15.523-.15.87 0 .578.13 1.075.39 1.488.222.424.475.823.76 1.197-.92.026-2.075.142-3.466.346-1.405.244-2.746.73-4.02 1.458-1.138.678-1.924 1.465-2.356 2.36-.447.894-.67 1.71-.67 2.437 0 1.497.688 2.782 2.06 3.857 1.362 1.15 3.422 1.738 6.182 1.763 3.297-.05 5.82-.837 7.567-2.363 1.686-1.475 2.53-3.166 2.53-5.076-.026-1.343-.333-2.432-.923-3.266a13.466 13.466 0 0 0-2.184-2.247l-1.337-1.096a7.868 7.868 0 0 1-.596-.672c-.242-.27-.362-.607-.362-1.02 0-.422.118-.793.352-1.113.2-.31.416-.584.652-.827.41-.36.797-.712 1.16-1.057.33-.345.64-.723.933-1.133.6-.846.912-1.974.935-3.383 0-.77-.087-1.442-.26-2.018a7.594 7.594 0 0 0-1.596-2.633 5.256 5.256 0 0 0-.836-.673h2.43L20.14 1h-7.88zm12.418.17v4.134h-4.134v2.003h4.134v4.134h2.004V7.308h4.134V5.304h-4.134V1.17h-2.004zM9.823 2.304c.807.026 1.52.265 2.14.712.61.475 1.1 1.093 1.474 1.85.794 1.577 1.192 3.145 1.192 4.697 0 .36-.03.803-.088 1.33a4.06 4.06 0 0 1-.643 1.54c-.69.706-1.555 1.08-2.593 1.118-.82 0-1.55-.25-2.19-.752-.64-.5-1.16-1.11-1.563-1.83-.834-1.54-1.25-3.022-1.25-4.447a4.844 4.844 0 0 1 .856-2.927c.708-.835 1.595-1.265 2.663-1.29zm1.667 17.54c.09 0 .16.003.21.01.337 0 .632.01.884.038 1.47 1.026 2.548 1.89 3.24 2.595.65.743.976 1.613.976 2.613 0 1.233-.477 2.238-1.43 3.02-.978.795-2.396 1.205-4.253 1.23-2.07-.025-3.702-.486-4.894-1.382-1.255-.897-1.88-2.04-1.88-3.423 0-.704.143-1.307.433-1.806a4.11 4.11 0 0 1 .92-1.194 5.237 5.237 0 0 1 1.11-.71c.365-.155.646-.27.847-.347a16.935 16.935 0 0 1 2.504-.56c.62-.056 1.065-.085 1.333-.085z" id="path4713" opacity=".5"/></svg>
diff --git a/apps/federatedfilesharing/templates/settings-personal.php b/apps/federatedfilesharing/templates/settings-personal.php
index 89f7b1eb1e7..ac893cc2599 100644
--- a/apps/federatedfilesharing/templates/settings-personal.php
+++ b/apps/federatedfilesharing/templates/settings-personal.php
@@ -28,10 +28,6 @@ style('federatedfilesharing', 'settings-personal');
data-url='https://twitter.com/intent/tweet?text=<?php p(urlencode($_['message_with_URL'])); ?>'>
Twitter
</button>
- <button class="social-googleplus pop-up"
- data-url='https://plus.google.com/share?url=<?php p(urlencode($_['reference'])); ?>'>
- Google+
- </button>
<button class="social-diaspora pop-up"
data-url='https://sharetodiaspora.github.io/?title=<?php p($_['message_without_URL']); ?>&url=<?php p(urlencode($_['reference'])); ?>'>
Diaspora
diff --git a/apps/user_ldap/lib/Connection.php b/apps/user_ldap/lib/Connection.php
index ba393dffc12..4335f8e4397 100644
--- a/apps/user_ldap/lib/Connection.php
+++ b/apps/user_ldap/lib/Connection.php
@@ -62,6 +62,9 @@ use OCP\ILogger;
* @property string ldapEmailAttribute
* @property string ldapExtStorageHomeAttribute
* @property string homeFolderNamingRule
+ * @property bool|string ldapNestedGroups
+ * @property string[] ldapBaseGroups
+ * @property string ldapGroupFilter
*/
class Connection extends LDAPUtility {
private $ldapConnectionRes = null;
diff --git a/apps/user_ldap/lib/Group_LDAP.php b/apps/user_ldap/lib/Group_LDAP.php
index 2240c2ad229..1658807c0dd 100644
--- a/apps/user_ldap/lib/Group_LDAP.php
+++ b/apps/user_ldap/lib/Group_LDAP.php
@@ -58,6 +58,11 @@ class Group_LDAP extends BackendUtility implements \OCP\GroupInterface, IGroupLD
*/
protected $cachedGroupsByMember;
+ /**
+ * @var string[] $cachedNestedGroups array of groups with gid (DN) as key
+ */
+ protected $cachedNestedGroups;
+
/** @var GroupPluginManager */
protected $groupPluginManager;
@@ -71,6 +76,7 @@ class Group_LDAP extends BackendUtility implements \OCP\GroupInterface, IGroupLD
$this->cachedGroupMembers = new CappedMemoryCache();
$this->cachedGroupsByMember = new CappedMemoryCache();
+ $this->cachedNestedGroups = new CappedMemoryCache();
$this->groupPluginManager = $groupPluginManager;
}
@@ -212,12 +218,12 @@ class Group_LDAP extends BackendUtility implements \OCP\GroupInterface, IGroupLD
*/
private function _groupMembers($dnGroup, &$seen = null) {
if ($seen === null) {
- $seen = array();
+ $seen = [];
}
- $allMembers = array();
+ $allMembers = [];
if (array_key_exists($dnGroup, $seen)) {
// avoid loops
- return array();
+ return [];
}
// used extensively in cron job, caching makes sense for nested groups
$cacheKey = '_groupMembers'.$dnGroup;
@@ -226,19 +232,12 @@ class Group_LDAP extends BackendUtility implements \OCP\GroupInterface, IGroupLD
return $groupMembers;
}
$seen[$dnGroup] = 1;
- $members = $this->access->readAttribute($dnGroup, $this->access->connection->ldapGroupMemberAssocAttr,
- $this->access->connection->ldapGroupFilter);
+ $members = $this->access->readAttribute($dnGroup, $this->access->connection->ldapGroupMemberAssocAttr);
if (is_array($members)) {
- foreach ($members as $member) {
- $allMembers[$member] = 1;
- $nestedGroups = $this->access->connection->ldapNestedGroups;
- if (!empty($nestedGroups)) {
- $subMembers = $this->_groupMembers($member, $seen);
- if ($subMembers) {
- $allMembers += $subMembers;
- }
- }
- }
+ $fetcher = function($memberDN, &$seen) {
+ return $this->_groupMembers($memberDN, $seen);
+ };
+ $allMembers = $this->walkNestedGroups($dnGroup, $fetcher, $members);
}
$allMembers += $this->getDynamicGroupMembers($dnGroup);
@@ -251,30 +250,69 @@ class Group_LDAP extends BackendUtility implements \OCP\GroupInterface, IGroupLD
* @param string $DN
* @param array|null &$seen
* @return array
+ * @throws \OC\ServerNotAvailableException
*/
- private function _getGroupDNsFromMemberOf($DN, &$seen = null) {
- if ($seen === null) {
- $seen = array();
- }
- if (array_key_exists($DN, $seen)) {
- // avoid loops
- return array();
- }
- $seen[$DN] = 1;
+ private function _getGroupDNsFromMemberOf($DN) {
$groups = $this->access->readAttribute($DN, 'memberOf');
if (!is_array($groups)) {
- return array();
+ return [];
+ }
+
+ $fetcher = function($groupDN) {
+ if (isset($this->cachedNestedGroups[$groupDN])) {
+ $nestedGroups = $this->cachedNestedGroups[$groupDN];
+ } else {
+ $nestedGroups = $this->access->readAttribute($groupDN, 'memberOf');
+ if (!is_array($nestedGroups)) {
+ $nestedGroups = [];
+ }
+ $this->cachedNestedGroups[$groupDN] = $nestedGroups;
+ }
+ return $nestedGroups;
+ };
+
+ $groups = $this->walkNestedGroups($DN, $fetcher, $groups);
+ return $this->access->groupsMatchFilter($groups);
+ }
+
+ /**
+ * @param string $dn
+ * @param \Closure $fetcher args: string $dn, array $seen, returns: string[] of dns
+ * @param array $list
+ * @return array
+ */
+ private function walkNestedGroups(string $dn, \Closure $fetcher, array $list): array {
+ $nesting = (int) $this->access->connection->ldapNestedGroups;
+ // depending on the input, we either have a list of DNs or a list of LDAP records
+ // also, the output expects either DNs or records. Testing the first element should suffice.
+ $recordMode = is_array($list) && isset($list[0]) && is_array($list[0]) && isset($list[0]['dn'][0]);
+
+ if ($nesting !== 1) {
+ if($recordMode) {
+ // the keys are numeric, but should hold the DN
+ return array_reduce($list, function ($transformed, $record) use ($dn) {
+ if($record['dn'][0] != $dn) {
+ $transformed[$record['dn'][0]] = $record;
+ }
+ return $transformed;
+ }, []);
+ }
+ return $list;
}
- $groups = $this->access->groupsMatchFilter($groups);
- $allGroups = $groups;
- $nestedGroups = $this->access->connection->ldapNestedGroups;
- if ((int)$nestedGroups === 1) {
- foreach ($groups as $group) {
- $subGroups = $this->_getGroupDNsFromMemberOf($group, $seen);
- $allGroups = array_merge($allGroups, $subGroups);
+
+ $seen = [];
+ while ($record = array_pop($list)) {
+ $recordDN = $recordMode ? $record['dn'][0] : $record;
+ if ($recordDN === $dn || array_key_exists($recordDN, $seen)) {
+ // Prevent loops
+ continue;
}
+ $fetched = $fetcher($record, $seen);
+ $list = array_merge($list, $fetched);
+ $seen[$recordDN] = $record;
}
- return $allGroups;
+
+ return $recordMode ? $seen : array_keys($seen);
}
/**
@@ -737,34 +775,28 @@ class Group_LDAP extends BackendUtility implements \OCP\GroupInterface, IGroupLD
*/
private function getGroupsByMember($dn, &$seen = null) {
if ($seen === null) {
- $seen = array();
+ $seen = [];
}
- $allGroups = array();
if (array_key_exists($dn, $seen)) {
// avoid loops
- return array();
+ return [];
}
+ $allGroups = [];
$seen[$dn] = true;
- $filter = $this->access->combineFilterWithAnd(array(
- $this->access->connection->ldapGroupFilter,
- $this->access->connection->ldapGroupMemberAssocAttr.'='.$dn
- ));
+ $filter = $this->access->connection->ldapGroupMemberAssocAttr.'='.$dn;
$groups = $this->access->fetchListOfGroups($filter,
- array($this->access->connection->ldapGroupDisplayName, 'dn'));
+ [$this->access->connection->ldapGroupDisplayName, 'dn']);
if (is_array($groups)) {
- foreach ($groups as $groupobj) {
- $groupDN = $groupobj['dn'][0];
- $allGroups[$groupDN] = $groupobj;
- $nestedGroups = $this->access->connection->ldapNestedGroups;
- if (!empty($nestedGroups)) {
- $supergroups = $this->getGroupsByMember($groupDN, $seen);
- if (is_array($supergroups) && (count($supergroups)>0)) {
- $allGroups = array_merge($allGroups, $supergroups);
- }
+ $fetcher = function ($dn, &$seen) {
+ if(is_array($dn) && isset($dn['dn'][0])) {
+ $dn = $dn['dn'][0];
}
- }
+ return $this->getGroupsByMember($dn, $seen);
+ };
+ $allGroups = $this->walkNestedGroups($dn, $fetcher, $groups);
}
- return $allGroups;
+ $visibleGroups = $this->access->groupsMatchFilter(array_keys($allGroups));
+ return array_intersect_key($allGroups, array_flip($visibleGroups));
}
/**
@@ -811,7 +843,7 @@ class Group_LDAP extends BackendUtility implements \OCP\GroupInterface, IGroupLD
$primaryUsers = $this->getUsersInPrimaryGroup($groupDN, $search, $limit, $offset);
$posixGroupUsers = $this->getUsersInGidNumber($groupDN, $search, $limit, $offset);
- $members = array_keys($this->_groupMembers($groupDN));
+ $members = $this->_groupMembers($groupDN);
if(!$members && empty($posixGroupUsers) && empty($primaryUsers)) {
//in case users could not be retrieved, return empty result set
$this->access->connection->writeToCache($cacheKey, []);
@@ -886,7 +918,7 @@ class Group_LDAP extends BackendUtility implements \OCP\GroupInterface, IGroupLD
return false;
}
- $members = array_keys($this->_groupMembers($groupDN));
+ $members = $this->_groupMembers($groupDN);
$primaryUserCount = $this->countUsersInPrimaryGroup($groupDN, '');
if(!$members && $primaryUserCount === 0) {
//in case users could not be retrieved, return empty result set
diff --git a/apps/user_ldap/tests/Group_LDAPTest.php b/apps/user_ldap/tests/Group_LDAPTest.php
index ec8d888e2a0..cb5760e3171 100644
--- a/apps/user_ldap/tests/Group_LDAPTest.php
+++ b/apps/user_ldap/tests/Group_LDAPTest.php
@@ -98,16 +98,27 @@ class Group_LDAPTest extends TestCase {
public function testCountEmptySearchString() {
$access = $this->getAccessMock();
$pluginManager = $this->getPluginManagerMock();
+ $groupDN = 'cn=group,dc=foo,dc=bar';
$this->enableGroups($access);
$access->expects($this->any())
->method('groupname2dn')
- ->will($this->returnValue('cn=group,dc=foo,dc=bar'));
+ ->will($this->returnValue($groupDN));
$access->expects($this->any())
->method('readAttribute')
- ->will($this->returnValue(array('u11', 'u22', 'u33', 'u34')));
+ ->willReturnCallback(function($dn) use ($groupDN) {
+ if($dn === $groupDN) {
+ return [
+ 'uid=u11,ou=users,dc=foo,dc=bar',
+ 'uid=u22,ou=users,dc=foo,dc=bar',
+ 'uid=u33,ou=users,dc=foo,dc=bar',
+ 'uid=u34,ou=users,dc=foo,dc=bar'
+ ];
+ }
+ return [];
+ });
// for primary groups
$access->expects($this->once())
@@ -132,7 +143,7 @@ class Group_LDAPTest extends TestCase {
$access->expects($this->any())
->method('fetchListOfUsers')
- ->will($this->returnValue(array()));
+ ->will($this->returnValue([]));
$access->expects($this->any())
->method('readAttribute')
@@ -145,7 +156,7 @@ class Group_LDAPTest extends TestCase {
if(strpos($name, 'u') === 0) {
return strpos($name, '3');
}
- return array('u11', 'u22', 'u33', 'u34');
+ return ['u11', 'u22', 'u33', 'u34'];
}));
$access->expects($this->any())
@@ -625,7 +636,7 @@ class Group_LDAPTest extends TestCase {
->method('dn2groupname')
->will($this->returnArgument(0));
- $access->expects($this->exactly(3))
+ $access->expects($this->exactly(1))
->method('groupsMatchFilter')
->will($this->returnArgument(0));
@@ -659,14 +670,15 @@ class Group_LDAPTest extends TestCase {
$access->expects($this->once())
->method('username2dn')
->will($this->returnValue($dn));
-
$access->expects($this->never())
->method('readAttribute')
->with($dn, 'memberOf');
-
$access->expects($this->once())
->method('nextcloudGroupNames')
->will($this->returnValue([]));
+ $access->expects($this->any())
+ ->method('groupsMatchFilter')
+ ->willReturnArgument(0);
$groupBackend = new GroupLDAP($access, $pluginManager);
$groupBackend->getUserGroups('userX');
@@ -680,12 +692,15 @@ class Group_LDAPTest extends TestCase {
$access->connection->expects($this->any())
->method('__get')
->will($this->returnCallback(function($name) {
- if($name === 'useMemberOfToDetectMembership') {
- return 0;
- } else if($name === 'ldapDynamicGroupMemberURL') {
- return '';
- } else if($name === 'ldapNestedGroups') {
- return false;
+ switch($name) {
+ case 'useMemberOfToDetectMembership':
+ return 0;
+ case 'ldapDynamicGroupMemberURL':
+ return '';
+ case 'ldapNestedGroups':
+ return false;
+ case 'ldapGroupMemberAssocAttr':
+ return 'member';
}
return 1;
}));
@@ -716,10 +731,12 @@ class Group_LDAPTest extends TestCase {
->method('nextcloudGroupNames')
->with([$group1, $group2])
->will($this->returnValue(['group1', 'group2']));
-
$access->expects($this->once())
->method('fetchListOfGroups')
->will($this->returnValue([$group1, $group2]));
+ $access->expects($this->any())
+ ->method('groupsMatchFilter')
+ ->willReturnArgument(0);
$groupBackend = new GroupLDAP($access, $pluginManager);
$groups = $groupBackend->getUserGroups('userX');
@@ -999,14 +1016,6 @@ class Group_LDAPTest extends TestCase {
$groups1,
['cn=Birds,' . $base => $groups1]
],
- [ #2 – test uids with nested groups
- 'cn=Birds,' . $base,
- $expGroups2,
- [
- 'cn=Birds,' . $base => $groups1,
- '8427' => $groups2Nested, // simplified - nested groups would work with DNs
- ],
- ],
];
}
@@ -1045,9 +1054,7 @@ class Group_LDAPTest extends TestCase {
$ldap = new GroupLDAP($access, $pluginManager);
$resultingMembers = $this->invokePrivate($ldap, '_groupMembers', [$groupDN]);
- $expected = array_keys(array_flip($expectedMembers));
-
- $this->assertEquals($expected, array_keys($resultingMembers), '', 0.0, 10, true);
+ $this->assertEquals($expectedMembers, $resultingMembers, '', 0.0, 10, true);
}
}
diff --git a/build/integration/features/bootstrap/LDAPContext.php b/build/integration/features/bootstrap/LDAPContext.php
index ee7acab6f5f..2ad737bf8b8 100644
--- a/build/integration/features/bootstrap/LDAPContext.php
+++ b/build/integration/features/bootstrap/LDAPContext.php
@@ -27,6 +27,7 @@ use PHPUnit\Framework\Assert;
class LDAPContext implements Context {
use BasicStructure;
+ use CommandLine;
protected $configID;
@@ -37,6 +38,8 @@ class LDAPContext implements Context {
if($this->configID === null) {
return;
}
+ $this->disableLDAPConfiguration(); # via occ in case of big config issues
+ $this->asAn('admin');
$this->sendingTo('DELETE', $this->apiUrl . '/' . $this->configID);
}
@@ -196,4 +199,9 @@ class LDAPContext implements Context {
$backend = (string)simplexml_load_string($this->response->getBody())->data[0]->backend;
Assert::assertEquals('LDAP', $backend);
}
+
+ public function disableLDAPConfiguration() {
+ $configKey = $this->configID . 'ldap_configuration_active';
+ $this->invokingTheCommand('config:app:set user_ldap ' . $configKey . ' --value="0"');
+ }
}
diff --git a/build/integration/ldap_features/ldap-openldap.feature b/build/integration/ldap_features/ldap-openldap.feature
index 4b0b02c5b4f..6c5ed8b462b 100644
--- a/build/integration/ldap_features/ldap-openldap.feature
+++ b/build/integration/ldap_features/ldap-openldap.feature
@@ -102,3 +102,67 @@ Feature: LDAP
| ldapHost | foo.bar |
| ldapPort | 2456 |
Then Expect ServerException on failed web login as "alice"
+
+ Scenario: Test LDAP group membership with intermediate groups not matching filter
+ Given modify LDAP configuration
+ | ldapBaseGroups | ou=OtherGroups,dc=nextcloud,dc=ci |
+ | ldapGroupFilter | (&(cn=Gardeners)(objectclass=groupOfNames)) |
+ | ldapNestedGroups | 1 |
+ | useMemberOfToDetectMembership | 1 |
+ | ldapUserFilter | (&(objectclass=inetorgperson)(!(uid=alice))) |
+ | ldapExpertUsernameAttr | uid |
+ | ldapGroupMemberAssocAttr | member |
+ And As an "admin"
+ # for population
+ And sending "GET" to "/cloud/groups"
+ And sending "GET" to "/cloud/groups/Gardeners/users"
+ Then the OCS status code should be "200"
+ And the "users" result should match
+ | alice | 0 |
+ | clara | 1 |
+ | elisa | 1 |
+ | gustaf | 1 |
+ | jesper | 1 |
+
+ Scenario: Test LDAP group membership with intermediate groups not matching filter and without memberof
+ Given modify LDAP configuration
+ | ldapBaseGroups | ou=OtherGroups,dc=nextcloud,dc=ci |
+ | ldapGroupFilter | (&(cn=Gardeners)(objectclass=groupOfNames)) |
+ | ldapNestedGroups | 1 |
+ | useMemberOfToDetectMembership | 0 |
+ | ldapUserFilter | (&(objectclass=inetorgperson)(!(uid=alice))) |
+ | ldapExpertUsernameAttr | uid |
+ | ldapGroupMemberAssocAttr | member |
+ And As an "admin"
+ # for population
+ And sending "GET" to "/cloud/groups"
+ And sending "GET" to "/cloud/groups/Gardeners/users"
+ Then the OCS status code should be "200"
+ And the "users" result should match
+ | alice | 0 |
+ | clara | 1 |
+ | elisa | 1 |
+ | gustaf | 1 |
+ | jesper | 1 |
+
+ Scenario: Test LDAP group membership with intermediate groups not matching filter, numeric group ids
+ Given modify LDAP configuration
+ | ldapBaseGroups | ou=NumericGroups,dc=nextcloud,dc=ci |
+ | ldapGroupFilter | (&(cn=2000)(objectclass=groupOfNames)) |
+ | ldapNestedGroups | 1 |
+ | useMemberOfToDetectMembership | 1 |
+ | ldapUserFilter | (&(objectclass=inetorgperson)(!(uid=alice))) |
+ | ldapExpertUsernameAttr | uid |
+ | ldapGroupMemberAssocAttr | member |
+ And As an "admin"
+ # for population
+ And sending "GET" to "/cloud/groups"
+ And sending "GET" to "/cloud/groups/2000/users"
+ Then the OCS status code should be "200"
+ And the "users" result should match
+ | alice | 0 |
+ | clara | 1 |
+ | elisa | 1 |
+ | gustaf | 1 |
+ | jesper | 1 |
+
diff --git a/build/integration/ldap_features/openldap-numerical-id.feature b/build/integration/ldap_features/openldap-numerical-id.feature
index 2d87ba33e6e..4112df0ae1a 100644
--- a/build/integration/ldap_features/openldap-numerical-id.feature
+++ b/build/integration/ldap_features/openldap-numerical-id.feature
@@ -29,3 +29,39 @@ Scenario: Test by logging in
And Logging in using web as "92379"
And Sending a "GET" to "/remote.php/webdav/welcome.txt" with requesttoken
Then the HTTP status code should be "200"
+
+Scenario: Test LDAP group retrieval with numeric group ids and nesting
+ # Nesting does not play a role here really
+ Given modify LDAP configuration
+ | ldapBaseGroups | ou=NumericGroups,dc=nextcloud,dc=ci |
+ | ldapGroupFilter | (objectclass=groupOfNames) |
+ | ldapNestedGroups | 1 |
+ | useMemberOfToDetectMembership | 1 |
+ And As an "admin"
+ And sending "GET" to "/cloud/groups"
+ Then the OCS status code should be "200"
+ And the "groups" result should match
+ | 2000 | 1 |
+ | 3000 | 1 |
+ | 3001 | 1 |
+ | 3002 | 1 |
+
+Scenario: Test LDAP group membership with intermediate groups not matching filter, numeric group ids
+ Given modify LDAP configuration
+ | ldapBaseGroups | ou=NumericGroups,dc=nextcloud,dc=ci |
+ | ldapGroupFilter | (&(cn=2000)(objectclass=groupOfNames)) |
+ | ldapNestedGroups | 1 |
+ | useMemberOfToDetectMembership | 1 |
+ | ldapUserFilter | (&(objectclass=inetorgperson)(!(uid=alice))) |
+ | ldapGroupMemberAssocAttr | member |
+ And As an "admin"
+ # for population
+ And sending "GET" to "/cloud/groups"
+ And sending "GET" to "/cloud/groups/2000/users"
+ Then the OCS status code should be "200"
+ And the "users" result should match
+ | 92379 | 0 |
+ | 54172 | 1 |
+ | 50194 | 1 |
+ | 59376 | 1 |
+ | 59463 | 1 |
diff --git a/lib/private/App/AppStore/Bundles/SocialSharingBundle.php b/lib/private/App/AppStore/Bundles/SocialSharingBundle.php
index c882a8df557..59ea3e08cf2 100644
--- a/lib/private/App/AppStore/Bundles/SocialSharingBundle.php
+++ b/lib/private/App/AppStore/Bundles/SocialSharingBundle.php
@@ -38,7 +38,6 @@ class SocialSharingBundle extends Bundle {
public function getAppIdentifiers() {
return [
'socialsharing_twitter',
- 'socialsharing_googleplus',
'socialsharing_facebook',
'socialsharing_email',
'socialsharing_diaspora',
diff --git a/lib/private/Files/Cache/Cache.php b/lib/private/Files/Cache/Cache.php
index 7b42cc2aa57..ef1f0f3c870 100644
--- a/lib/private/Files/Cache/Cache.php
+++ b/lib/private/Files/Cache/Cache.php
@@ -3,6 +3,7 @@
* @copyright Copyright (c) 2016, ownCloud, Inc.
*
* @author Andreas Fischer <bantu@owncloud.com>
+ * @author Ari Selseng <ari@selseng.net>
* @author Artem Kochnev <MrJeos@gmail.com>
* @author Björn Schießle <bjoern@schiessle.org>
* @author Florin Peter <github@florin-peter.de>
@@ -774,15 +775,38 @@ class Cache implements ICache {
* @param string|boolean $path
* @param array $data (optional) meta data of the folder
*/
- public function correctFolderSize($path, $data = null) {
+ public function correctFolderSize($path, $data = null, $isBackgroundScan = false) {
$this->calculateFolderSize($path, $data);
if ($path !== '') {
$parent = dirname($path);
if ($parent === '.' or $parent === '/') {
$parent = '';
}
- $this->correctFolderSize($parent);
+ if ($isBackgroundScan) {
+ $parentData = $this->get($parent);
+ if ($parentData['size'] !== -1 && $this->getIncompleteChildrenCount($parentData['fileid']) === 0) {
+ $this->correctFolderSize($parent, $parentData, $isBackgroundScan);
+ }
+ } else {
+ $this->correctFolderSize($parent);
+ }
+ }
+ }
+
+ /**
+ * get the incomplete count that shares parent $folder
+ *
+ * @param int $fileId the file id of the folder
+ * @return int
+ */
+ public function getIncompleteChildrenCount($fileId) {
+ if ($fileId > -1) {
+ $sql = 'SELECT count(*)
+ FROM `*PREFIX*filecache` WHERE `parent` = ? AND size = -1';
+ $result = $this->connection->executeQuery($sql, [$fileId]);
+ return (int)$result->fetchColumn();
}
+ return -1;
}
/**
@@ -919,4 +943,4 @@ class Cache implements ICache {
return trim(\OC_Util::normalizeUnicode($path), '/');
}
-}
+} \ No newline at end of file
diff --git a/lib/private/Files/Cache/Scanner.php b/lib/private/Files/Cache/Scanner.php
index ca9a0b794f9..e684b853103 100644
--- a/lib/private/Files/Cache/Scanner.php
+++ b/lib/private/Files/Cache/Scanner.php
@@ -532,7 +532,7 @@ class Scanner extends BasicEmitter implements IScanner {
$callback();
\OC_Hook::emit('Scanner', 'correctFolderSize', array('path' => $path));
if ($this->cacheActive && $this->cache instanceof Cache) {
- $this->cache->correctFolderSize($path);
+ $this->cache->correctFolderSize($path, null, true);
}
} catch (\OCP\Files\StorageInvalidException $e) {
// skip unavailable storages
diff --git a/lib/private/Files/Cache/Wrapper/CacheJail.php b/lib/private/Files/Cache/Wrapper/CacheJail.php
index 57f58e7d839..7e113d13678 100644
--- a/lib/private/Files/Cache/Wrapper/CacheJail.php
+++ b/lib/private/Files/Cache/Wrapper/CacheJail.php
@@ -265,9 +265,9 @@ class CacheJail extends CacheWrapper {
* @param string|boolean $path
* @param array $data (optional) meta data of the folder
*/
- public function correctFolderSize($path, $data = null) {
+ public function correctFolderSize($path, $data = null, $isBackgroundSize = false) {
if ($this->getCache() instanceof Cache) {
- $this->getCache()->correctFolderSize($this->getSourcePath($path), $data);
+ $this->getCache()->correctFolderSize($this->getSourcePath($path), $data, $isBackgroundSize);
}
}
diff --git a/lib/private/Files/Cache/Wrapper/CacheWrapper.php b/lib/private/Files/Cache/Wrapper/CacheWrapper.php
index da0a1b54e7f..223e678f323 100644
--- a/lib/private/Files/Cache/Wrapper/CacheWrapper.php
+++ b/lib/private/Files/Cache/Wrapper/CacheWrapper.php
@@ -258,9 +258,9 @@ class CacheWrapper extends Cache {
* @param string|boolean $path
* @param array $data (optional) meta data of the folder
*/
- public function correctFolderSize($path, $data = null) {
+ public function correctFolderSize($path, $data = null, $isBackgroundScan = false) {
if ($this->getCache() instanceof Cache) {
- $this->getCache()->correctFolderSize($path, $data);
+ $this->getCache()->correctFolderSize($path, $data, $isBackgroundScan);
}
}
diff --git a/tests/lib/App/AppStore/Bundles/SocialSharingBundleTest.php b/tests/lib/App/AppStore/Bundles/SocialSharingBundleTest.php
index 02ea0eb6ae5..3d8bccf1935 100644
--- a/tests/lib/App/AppStore/Bundles/SocialSharingBundleTest.php
+++ b/tests/lib/App/AppStore/Bundles/SocialSharingBundleTest.php
@@ -31,7 +31,6 @@ class SocialSharingBundleTest extends BundleBase {
$this->bundleName = 'Social sharing bundle';
$this->bundleAppIds = [
'socialsharing_twitter',
- 'socialsharing_googleplus',
'socialsharing_facebook',
'socialsharing_email',
'socialsharing_diaspora',
diff --git a/tests/lib/Files/Cache/ScannerTest.php b/tests/lib/Files/Cache/ScannerTest.php
index 736df2174d1..0f5335f4416 100644
--- a/tests/lib/Files/Cache/ScannerTest.php
+++ b/tests/lib/Files/Cache/ScannerTest.php
@@ -203,6 +203,44 @@ class ScannerTest extends \Test\TestCase {
$this->assertFalse($this->cache->getIncomplete());
}
+ public function testBackgroundScanNestedIncompleteFolders() {
+ $this->storage->mkdir('folder');
+ $this->scanner->backgroundScan();
+
+ $this->storage->mkdir('folder/subfolder1');
+ $this->storage->mkdir('folder/subfolder2');
+
+ $this->storage->mkdir('folder/subfolder1/subfolder3');
+ $this->cache->put('folder', ['size' => -1]);
+ $this->cache->put('folder/subfolder1', ['size' => -1]);
+
+ // do a scan to get the folders into the cache.
+ $this->scanner->backgroundScan();
+
+ $this->assertTrue($this->cache->inCache('folder/subfolder1/subfolder3'));
+
+ $this->storage->file_put_contents('folder/subfolder1/bar1.txt', 'foobar');
+ $this->storage->file_put_contents('folder/subfolder1/subfolder3/bar3.txt', 'foobar');
+ $this->storage->file_put_contents('folder/subfolder2/bar2.txt', 'foobar');
+
+ //mark folders as incomplete.
+ $this->cache->put('folder/subfolder1', ['size' => -1]);
+ $this->cache->put('folder/subfolder2', ['size' => -1]);
+ $this->cache->put('folder/subfolder1/subfolder3', ['size' => -1]);
+
+ $this->scanner->backgroundScan();
+
+ $this->assertTrue($this->cache->inCache('folder/subfolder1/bar1.txt'));
+ $this->assertTrue($this->cache->inCache('folder/subfolder2/bar2.txt'));
+ $this->assertTrue($this->cache->inCache('folder/subfolder1/subfolder3/bar3.txt'));
+
+ //check if folder sizes are correct.
+ $this->assertEquals(18, $this->cache->get('folder')['size']);
+ $this->assertEquals(12, $this->cache->get('folder/subfolder1')['size']);
+ $this->assertEquals(6, $this->cache->get('folder/subfolder1/subfolder3')['size']);
+ $this->assertEquals(6, $this->cache->get('folder/subfolder2')['size']);
+ }
+
public function testReuseExisting() {
$this->fillTestFolders();