diff --git a/apps/user_ldap/group_ldap.php b/apps/user_ldap/group_ldap.php
index 32e2cec5960ade394ef892a661e2a9b6d1f8c4eb..cef9ca3c4cff1f9258b121a9a42c802045b37049 100644
--- a/apps/user_ldap/group_ldap.php
+++ b/apps/user_ldap/group_ldap.php
@@ -61,8 +61,7 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface {
 			return false;
 		}
 		//usually, LDAP attributes are said to be case insensitive. But there are exceptions of course.
-		$members = $this->access->readAttribute($dn_group,
-						$this->access->connection->ldapGroupMemberAssocAttr);
+		$members = array_keys($this->_groupMembers($dn_group));
 		if(!$members) {
 			$this->access->connection->writeToCache('inGroup'.$uid.':'.$gid, false);
 			return false;
@@ -89,6 +88,39 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface {
 		return $isInGroup;
 	}
 
+	private function _groupMembers($dnGroup, &$seen = null) {
+		if ($seen === null) {
+			$seen = array();
+		}
+		$allMembers = array();
+		if (array_key_exists($dnGroup, $seen)) {
+			// avoid loops
+			return array();
+		}
+		// used extensively in cron job, caching makes sense for nested groups
+		$cacheKey = '_groupMembers'.$dnGroup;
+		if($this->access->connection->isCached($cacheKey)) {
+			return $this->access->connection->getFromCache($cacheKey);
+		}
+		$seen[$dnGroup] = 1;
+		$members = $this->access->readAttribute($dnGroup, $this->access->connection->ldapGroupMemberAssocAttr,
+												$this->access->connection->ldapGroupFilter);
+		if (is_array($members)) {
+			foreach ($members as $memberDN) {
+				$allMembers[$memberDN] = 1;
+				$nestedGroups = $this->access->connection->ldapNestedGroups;
+				if (!empty($nestedGroups)) {
+					$subMembers = $this->_groupMembers($memberDN, $seen);
+					if ($subMembers) {
+						$allMembers = array_merge($allMembers, $subMembers);
+					}
+				}
+			}
+		}
+		$this->access->connection->writeToCache($cacheKey, $allMembers);
+		return $allMembers;
+	}
+
 	/**
 	 * @brief Get all groups a user belongs to
 	 * @param $uid Name of the user
@@ -124,18 +156,45 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface {
 			$uid = $userDN;
 		}
 
-		$filter = $this->access->combineFilterWithAnd(array(
-			$this->access->connection->ldapGroupFilter,
-			$this->access->connection->ldapGroupMemberAssocAttr.'='.$uid
-		));
-		$groups = $this->access->fetchListOfGroups($filter,
-				array($this->access->connection->ldapGroupDisplayName, 'dn'));
+		$groups = array_values($this->getGroupsByMember($uid));
 		$groups = array_unique($this->access->ownCloudGroupNames($groups), SORT_LOCALE_STRING);
 		$this->access->connection->writeToCache($cacheKey, $groups);
 
 		return $groups;
 	}
 
+	private function getGroupsByMember($dn, &$seen = null) {
+		if ($seen === null) {
+			$seen = array();
+		}
+		$allGroups = array();
+		if (array_key_exists($dn, $seen)) {
+		    // avoid loops
+		    return array();
+		}
+		$seen[$dn] = true;
+		$filter = $this->access->combineFilterWithAnd(array(
+			$this->access->connection->ldapGroupFilter,
+			$this->access->connection->ldapGroupMemberAssocAttr.'='.$dn
+		));
+		$groups = $this->access->fetchListOfGroups($filter,
+			array($this->access->connection->ldapGroupDisplayName, 'dn'));
+		if (is_array($groups)) {
+			foreach ($groups as $groupobj) {
+				$groupDN = $groupobj['dn'];
+				$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);
+					}
+				}
+			}
+		}
+		return $allGroups;
+	}
+
 	/**
 	 * @brief get a list of all users in a group
 	 * @returns array with user ids
@@ -172,8 +231,7 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface {
 			return array();
 		}
 
-		$members = $this->access->readAttribute($groupDN,
-						$this->access->connection->ldapGroupMemberAssocAttr);
+		$members = array_keys($this->_groupMembers($groupDN));
 		if(!$members) {
 			//in case users could not be retrieved, return empty resultset
 			$this->access->connection->writeToCache($cachekey, array());
diff --git a/apps/user_ldap/lib/configuration.php b/apps/user_ldap/lib/configuration.php
index 954d0501fadbf10e17f72c765df6ad425083e2f2..612a623e910eb178c7b751a58b31b9b0c8283311 100644
--- a/apps/user_ldap/lib/configuration.php
+++ b/apps/user_ldap/lib/configuration.php
@@ -76,6 +76,7 @@ class Configuration {
 		'ldapExpertUUIDUserAttr' => null,
 		'ldapExpertUUIDGroupAttr' => null,
 		'lastJpegPhotoLookup' => null,
+		'ldapNestedGroups' => false,
 	);
 
 	/**
@@ -342,6 +343,7 @@ class Configuration {
 			'ldap_expert_uuid_group_attr'		=> '',
 			'has_memberof_filter_support'		=> 0,
 			'last_jpegPhoto_lookup'				=> 0,
+			'ldap_nested_groups'		        => 0,
 		);
 	}
 
@@ -393,6 +395,7 @@ class Configuration {
 			'ldap_expert_uuid_group_attr'		=> 'ldapExpertUUIDGroupAttr',
 			'has_memberof_filter_support'		=> 'hasMemberOfFilterSupport',
 			'last_jpegPhoto_lookup'				=> 'lastJpegPhotoLookup',
+			'ldap_nested_groups'                    => 'ldapNestedGroups',
 		);
 		return $array;
 	}
diff --git a/apps/user_ldap/templates/settings.php b/apps/user_ldap/templates/settings.php
index 3ccc7a860f5aee6d3d80dbad2306e20a9e3c0c19..79c4ae224c3b6366fc214445fbc217e625fb6b81 100644
--- a/apps/user_ldap/templates/settings.php
+++ b/apps/user_ldap/templates/settings.php
@@ -36,6 +36,7 @@
 				<p><label for="ldap_base_groups"><?php p($l->t('Base Group Tree'));?></label><textarea id="ldap_base_groups" name="ldap_base_groups" placeholder="<?php p($l->t('One Group Base DN per line'));?>" data-default="<?php p($_['ldap_base_groups_default']); ?>" title="<?php p($l->t('Base Group Tree'));?>"></textarea></p>
 				<p><label for="ldap_attributes_for_group_search"><?php p($l->t('Group Search Attributes'));?></label><textarea id="ldap_attributes_for_group_search" name="ldap_attributes_for_group_search" placeholder="<?php p($l->t('Optional; one attribute per line'));?>" data-default="<?php p($_['ldap_attributes_for_group_search_default']); ?>" title="<?php p($l->t('Group Search Attributes'));?>"></textarea></p>
 				<p><label for="ldap_group_member_assoc_attribute"><?php p($l->t('Group-Member association'));?></label><select id="ldap_group_member_assoc_attribute" name="ldap_group_member_assoc_attribute" data-default="<?php p($_['ldap_group_member_assoc_attribute_default']); ?>" ><option value="uniqueMember"<?php if (isset($_['ldap_group_member_assoc_attribute']) && ($_['ldap_group_member_assoc_attribute'] === 'uniqueMember')) p(' selected'); ?>>uniqueMember</option><option value="memberUid"<?php if (isset($_['ldap_group_member_assoc_attribute']) && ($_['ldap_group_member_assoc_attribute'] === 'memberUid')) p(' selected'); ?>>memberUid</option><option value="member"<?php if (isset($_['ldap_group_member_assoc_attribute']) && ($_['ldap_group_member_assoc_attribute'] === 'member')) p(' selected'); ?>>member (AD)</option></select></p>
+				<p><label for="ldap_nested_groups"><?php p($l->t('Nested Groups'));?></label><input type="checkbox" id="ldap_nested_groups" name="ldap_nested_groups" value="1" data-default="<?php p($_['ldap_nested_groups_default']); ?>"  title="<?php p($l->t('When switched on, groups that contain groups are supported. (Only works if the group member attribute contains DNs.)'));?>" /></p>
 			</div>
 			<h3><?php p($l->t('Special Attributes'));?></h3>
 			<div>