diff --git a/apps/user_ldap/appinfo/app.php b/apps/user_ldap/appinfo/app.php
index c2cd295523eaf309190996c7879705f1df085398..a26c7709d41096b34cc16c8ae26dd5e018ee9a7c 100644
--- a/apps/user_ldap/appinfo/app.php
+++ b/apps/user_ldap/appinfo/app.php
@@ -26,8 +26,14 @@ OCP\App::registerAdmin('user_ldap', 'settings');
 $configPrefixes = OCA\user_ldap\lib\Helper::getServerConfigurationPrefixes(true);
 $ldapWrapper = new OCA\user_ldap\lib\LDAP();
 if(count($configPrefixes) === 1) {
+	$ocConfig = \OC::$server->getConfig();
+	$userManager = new OCA\user_ldap\lib\user\Manager($ocConfig,
+		new OCA\user_ldap\lib\FilesystemHelper(),
+		new OCA\user_ldap\lib\LogWrapper(),
+		\OC::$server->getAvatarManager(),
+		new \OCP\Image());
 	$connector = new OCA\user_ldap\lib\Connection($ldapWrapper, $configPrefixes[0]);
-	$ldapAccess = new OCA\user_ldap\lib\Access($connector, $ldapWrapper);
+	$ldapAccess = new OCA\user_ldap\lib\Access($connector, $ldapWrapper, $userManager);
 	$userBackend  = new OCA\user_ldap\USER_LDAP($ldapAccess);
 	$groupBackend = new OCA\user_ldap\GROUP_LDAP($ldapAccess);
 } else if(count($configPrefixes) > 1) {
diff --git a/apps/user_ldap/lib/access.php b/apps/user_ldap/lib/access.php
index 78de14f4ee9848c133d5ee610e5de2d6e444b70f..d488d2da1292ef6fbb3a6bd5df26d8a50816aa1c 100644
--- a/apps/user_ldap/lib/access.php
+++ b/apps/user_ldap/lib/access.php
@@ -27,20 +27,21 @@ namespace OCA\user_ldap\lib;
  * Class Access
  * @package OCA\user_ldap\lib
  */
-class Access extends LDAPUtility {
+class Access extends LDAPUtility implements user\IUserTools {
 	public $connection;
+	public $userManager;
 	//never ever check this var directly, always use getPagedSearchResultState
 	protected $pagedSearchedSuccessful;
 
 	protected $cookies = array();
 
-	/**
-	 * @param Connection $connection
-	 * @param ILDAPWrapper $ldap
-	 */
-	public function __construct(Connection $connection, ILDAPWrapper $ldap) {
+
+	public function __construct(Connection $connection, ILDAPWrapper $ldap,
+		user\Manager $userManager) {
 		parent::__construct($ldap);
 		$this->connection = $connection;
+		$this->userManager = $userManager;
+		$this->userManager->setLdapAccess($this);
 	}
 
 	/**
@@ -50,10 +51,18 @@ class Access extends LDAPUtility {
 		return ($this->connection instanceof Connection);
 	}
 
+	/**
+	 * returns the Connection instance
+	 * @return \OCA\user_ldap\lib\Connection
+	 */
+	public function getConnection() {
+		return $this->connection;
+	}
+
 	/**
 	 * reads a given attribute for an LDAP record identified by a DN
-	 * @param string $dn the record in question
-	 * @param string $attr the attribute that shall be retrieved
+	 * @param $dn the record in question
+	 * @param $attr the attribute that shall be retrieved
 	 *        if empty, just check the record's existence
 	 * @param string $filter
 	 * @return array|false an array of values on success or an empty
@@ -626,6 +635,12 @@ class Access extends LDAPUtility {
 			return false;
 		}
 
+		if($isUser) {
+			//make sure that email address is retrieved prior to login, so user
+			//will be notified when something is shared with him
+			$this->userManager->get($ocname)->update();
+		}
+
 		return true;
 	}
 
diff --git a/apps/user_ldap/lib/filesystemhelper.php b/apps/user_ldap/lib/filesystemhelper.php
new file mode 100644
index 0000000000000000000000000000000000000000..abf0e8a7b5935aa072b11a2c309a056c7899e740
--- /dev/null
+++ b/apps/user_ldap/lib/filesystemhelper.php
@@ -0,0 +1,46 @@
+<?php
+
+/**
+ * ownCloud – LDAP FilesystemHelper
+ *
+ * @author Arthur Schiwon
+ * @copyright 2014 Arthur Schiwon blizzz@owncloud.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library 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 library.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OCA\user_ldap\lib;
+
+/**
+ * @brief wraps around static ownCloud core methods
+ */
+class FilesystemHelper {
+
+	/**
+	 * @brief states whether the filesystem was loaded
+	 * @return bool
+	 */
+	public function isLoaded() {
+		return \OC\Files\Filesystem::$loaded;
+	}
+
+	/**
+	 * @brief initializes the filesystem for the given user
+	 * @param string the ownCloud username of the user
+	 */
+	public function setup($uid) {
+		\OC_Util::setupFS($uid);
+	}
+}
diff --git a/apps/user_ldap/lib/logwrapper.php b/apps/user_ldap/lib/logwrapper.php
new file mode 100644
index 0000000000000000000000000000000000000000..4826cb0358582d86506787c1b7b064eaf4323f52
--- /dev/null
+++ b/apps/user_ldap/lib/logwrapper.php
@@ -0,0 +1,39 @@
+<?php
+
+/**
+ * ownCloud – LDAP LogWrapper
+ *
+ * @author Arthur Schiwon
+ * @copyright 2014 Arthur Schiwon blizzz@owncloud.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library 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 library.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OCA\user_ldap\lib;
+
+/**
+ * @brief wraps around static ownCloud core methods
+ */
+class LogWrapper {
+	protected $app = 'user_ldap';
+
+	/**
+	 * @brief states whether the filesystem was loaded
+	 * @return bool
+	 */
+	public function log($msg, $level) {
+		\OCP\Util::writeLog($this->app, $msg, $level);
+	}
+}
diff --git a/apps/user_ldap/lib/proxy.php b/apps/user_ldap/lib/proxy.php
index d15d1ae861630197424d2bc47e42805093e80b99..73a52a7ddd9040eb75f593e9828292ed8d65684f 100644
--- a/apps/user_ldap/lib/proxy.php
+++ b/apps/user_ldap/lib/proxy.php
@@ -41,8 +41,21 @@ abstract class Proxy {
 	 * @param string $configPrefix
 	 */
 	private function addAccess($configPrefix) {
+		static $ocConfig;
+		static $fs;
+		static $log;
+		static $avatarM;
+		if(is_null($fs)) {
+			$ocConfig = \OC::$server->getConfig();
+			$fs       = new FilesystemHelper();
+			$log      = new LogWrapper();
+			$avatarM  = \OC::$server->getAvatarManager();
+		}
+		$userManager =
+			new user\Manager($ocConfig, $fs, $log, $avatarM, new \OCP\Image());
 		$connector = new Connection($this->ldap, $configPrefix);
-		self::$accesses[$configPrefix] = new Access($connector, $this->ldap);
+		self::$accesses[$configPrefix] =
+			new Access($connector, $this->ldap, $userManager);
 	}
 
 	/**
diff --git a/apps/user_ldap/lib/user/iusertools.php b/apps/user_ldap/lib/user/iusertools.php
new file mode 100644
index 0000000000000000000000000000000000000000..e409f3afed34460f03bfe63c475d4101ddfd365a
--- /dev/null
+++ b/apps/user_ldap/lib/user/iusertools.php
@@ -0,0 +1,40 @@
+<?php
+
+/**
+ * ownCloud – LDAP User
+ *
+ * @author Arthur Schiwon
+ * @copyright 2014 Arthur Schiwon blizzz@owncloud.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library 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 library.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OCA\user_ldap\lib\user;
+
+/**
+ * IUserTools
+ *
+ * defines methods that are required by User class for LDAP interaction
+ */
+interface IUserTools {
+	public function getConnection();
+
+	public function readAttribute($dn, $attr, $filter = 'objectClass=*');
+
+	public function dn2username($dn, $ldapname = null);
+
+	public function username2dn($name);
+
+}
diff --git a/apps/user_ldap/lib/user/manager.php b/apps/user_ldap/lib/user/manager.php
new file mode 100644
index 0000000000000000000000000000000000000000..6c635518d95552f945ee3b11c38194251dc16e8c
--- /dev/null
+++ b/apps/user_ldap/lib/user/manager.php
@@ -0,0 +1,167 @@
+<?php
+
+/**
+ * ownCloud – LDAP User
+ *
+ * @author Arthur Schiwon
+ * @copyright 2014 Arthur Schiwon blizzz@owncloud.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library 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 library.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OCA\user_ldap\lib\user;
+
+use OCA\user_ldap\lib\user\IUserTools;
+use OCA\user_ldap\lib\user\User;
+use OCA\user_ldap\lib\LogWrapper;
+use OCA\user_ldap\lib\FilesystemHelper;
+
+/**
+ * Manager
+ *
+ * upon request, returns an LDAP user object either by creating or from run-time
+ * cache
+ */
+class Manager {
+	/**
+	 * @var IUserTools
+	 */
+	protected $access;
+	/**
+	 * @var \OCP\IConfig
+	 */
+	protected $ocConfig;
+	/**
+	 * @var FilesystemHelper
+	 */
+	protected $ocFilesystem;
+	/**
+	 * @var LogWrapper
+	 */
+	protected $ocLog;
+	/**
+	 * @var \OCP\Image
+	 */
+	protected $image;
+	/**
+	 * @param \OCP\IAvatarManager
+	 */
+	protected $avatarManager;
+	/**
+	 * @var string[][]
+	 */
+	protected $users = array(
+		'byDN'  => array(),
+		'byUid' => array(),
+	);
+
+	/**
+	 * @brief Constructor
+	 * @param \OCP\IConfig respectively an instance that provides the methods
+	 * setUserValue and getUserValue as implemented in \OCP\Config
+	 * @param \OCA\user_ldap\lib\FilesystemHelper object that gives access to
+	 * necessary functions from the OC filesystem
+	 * @param  \OCA\user_ldap\lib\LogWrapper
+	 * @param \OCP\IAvatarManager
+	 * @param \OCP\Image an empty image instance
+	 * @throws Exception when the methods mentioned above do not exist
+	 */
+	public function __construct(\OCP\IConfig $ocConfig,
+		FilesystemHelper $ocFilesystem, LogWrapper $ocLog,
+		\OCP\IAvatarManager $avatarManager, \OCP\Image $image) {
+
+		if(!method_exists($ocConfig, 'setUserValue')
+		   || !method_exists($ocConfig, 'getUserValue')) {
+			throw new \Exception('Invalid ownCloud User Config object');
+		}
+		$this->ocConfig      = $ocConfig;
+		$this->ocFilesystem  = $ocFilesystem;
+		$this->ocLog         = $ocLog;
+		$this->avatarManager = $avatarManager;
+		$this->image         = $image;
+	}
+
+	/**
+	 * @brief binds manager to an instance of IUserTools (implemented by
+	 * Access). It needs to be assigned first before the manager can be used.
+	 * @param IUserTools
+	 */
+	public function setLdapAccess(IUserTools $access) {
+		$this->access = $access;
+	}
+
+	/**
+	 * @brief creates an instance of User and caches (just runtime) it in the
+	 * property array
+	 * @param string the DN of the user
+	 * @param string the internal (owncloud) username
+	 * @return \OCA\user_ldap\lib\User
+	 */
+	private function createAndCache($dn, $uid) {
+		$this->checkAccess();
+		$user = new User($uid, $dn, $this->access, $this->ocConfig,
+			$this->ocFilesystem, clone $this->image, $this->ocLog,
+			$this->avatarManager);
+		$users['byDN'][$dn]   = $user;
+		$users['byUid'][$uid] = $user;
+		return $user;
+	}
+
+	/**
+	 * @brief checks whether the Access instance has been set
+	 * @throws Exception if Access has not been set
+	 * @return null
+	 */
+	private function checkAccess() {
+		if(is_null($this->access)) {
+			throw new \Exception('LDAP Access instance must be set first');
+		}
+	}
+
+	/**
+	 * @brief returns a User object by it's DN or ownCloud username
+	 * @param string the DN or username of the user
+	 * @return \OCA\user_ldap\lib\User | null
+	 */
+	public function get($id) {
+		$this->checkAccess();
+		if(isset($this->users['byDN'][$id])) {
+			return $this->users['byDN'][$id];
+		} else if(isset($this->users['byUid'][$id])) {
+			return $this->users['byUid'][$id];
+		}
+
+		if(strpos(mb_strtolower($id, 'UTF-8'), 'dc=') === false) {
+			//most likely a uid
+			$dn = $this->access->username2dn($id);
+			if($dn !== false) {
+				return $this->createAndCache($dn, $id);
+			}
+		} else {
+			//so it's a DN
+			$uid = $this->access->dn2username($id);
+			if($uid !== false) {
+				return $this->createAndCache($id, $uid);
+			}
+		}
+		//either funny uid or invalid. Assume funny to be on the safe side.
+		$dn = $this->access->username2dn($id);
+		if($dn !== false) {
+			return $this->createAndCache($dn, $id);
+		}
+		return null;
+	}
+
+}
diff --git a/apps/user_ldap/lib/user/user.php b/apps/user_ldap/lib/user/user.php
new file mode 100644
index 0000000000000000000000000000000000000000..d4d2294307dbcc1ac15cedd567935b8ea2aad636
--- /dev/null
+++ b/apps/user_ldap/lib/user/user.php
@@ -0,0 +1,324 @@
+<?php
+
+/**
+ * ownCloud – LDAP User
+ *
+ * @author Arthur Schiwon
+ * @copyright 2014 Arthur Schiwon blizzz@owncloud.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library 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 library.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OCA\user_ldap\lib\user;
+
+use OCA\user_ldap\lib\user\IUserTools;
+use OCA\user_ldap\lib\Connection;
+use OCA\user_ldap\lib\FilesystemHelper;
+use OCA\user_ldap\lib\LogWrapper;
+
+/**
+ * User
+ *
+ * represents an LDAP user, gets and holds user-specific information from LDAP
+ */
+class User {
+	/**
+	 * @var IUserTools
+	 */
+	protected $access;
+	/**
+	 * @var Connection
+	 */
+	protected $connection;
+	/**
+	 * @var \OCP\IConfig
+	 */
+	protected $config;
+	/**
+	 * @var FilesystemHelper
+	 */
+	protected $fs;
+	/**
+	 * @var \OCP\Image
+	 */
+	protected $image;
+	/**
+	 * @var LogWrapper
+	 */
+	protected $log;
+	/**
+	 * @var \OCP\IAvatarManager
+	 */
+	protected $avatarManager;
+
+	/**
+	 * @var string
+	 */
+	protected $dn;
+	/**
+	 * @var string
+	 */
+	protected $uid;
+	/**
+	 * @var string[]
+	 */
+	protected $refreshedFeatures = array();
+	/**
+	 * @var string
+	 */
+	protected $avatarImage;
+
+	/**
+	 * DB config keys for user preferences
+	 */
+	const USER_PREFKEY_FIRSTLOGIN  = 'firstLoginAccomplished';
+	const USER_PREFKEY_LASTREFRESH = 'lastFeatureRefresh';
+
+	/**
+	 * @brief constructor, make sure the subclasses call this one!
+	 * @param string the internal username
+	 * @param string the LDAP DN
+	 * @param IUserTools $access an instance that implements IUserTools for
+	 * LDAP interaction
+	 * @param \OCP\Config
+	 * @param FilesystemHelper
+	 * @param \OCP\Image any empty instance
+	 * @param LogWrapper
+	 * @param \OCP\IAvatarManager
+	 */
+	public function __construct($username, $dn, IUserTools $access,
+		\OCP\IConfig $config, FilesystemHelper $fs, \OCP\Image $image,
+		LogWrapper $log, \OCP\IAvatarManager $avatarManager) {
+
+		$this->access        = $access;
+		$this->connection    = $access->getConnection();
+		$this->config        = $config;
+		$this->fs            = $fs;
+		$this->dn            = $dn;
+		$this->uid           = $username;
+		$this->image         = $image;
+		$this->log           = $log;
+		$this->avatarManager = $avatarManager;
+	}
+
+	/**
+	 * @brief updates properties like email, quota or avatar provided by LDAP
+	 * @return null
+	 */
+	public function update() {
+		if(is_null($this->dn)) {
+			return null;
+		}
+
+		$hasLoggedIn = $this->config->getUserValue($this->uid, 'user_ldap',
+				self::USER_PREFKEY_FIRSTLOGIN, 0);
+
+		if($this->needsRefresh()) {
+			$this->updateEmail();
+			$this->updateQuota();
+			if($hasLoggedIn !== 0) {
+				//we do not need to try it, when the user has not been logged in
+				//before, because the file system will not be ready.
+				$this->updateAvatar();
+				//in order to get an avatar as soon as possible, mark the user
+				//as refreshed only when updating the avatar did happen
+				$this->markRefreshTime();
+			}
+		}
+	}
+
+	/**
+	 * @brief returns the LDAP DN of the user
+	 * @return string
+	 */
+	public function getDN() {
+		return $this->dn;
+	}
+
+	/**
+	 * @brief returns the ownCloud internal username of the user
+	 * @return string
+	 */
+	public function getUsername() {
+		return $this->uid;
+	}
+
+	/**
+	 * @brief reads the image from LDAP that shall be used as Avatar
+	 * @return string data (provided by LDAP) | false
+	 */
+	public function getAvatarImage() {
+		if(!is_null($this->avatarImage)) {
+			return $this->avatarImage;
+		}
+
+		$this->avatarImage = false;
+		$attributes = array('jpegPhoto', 'thumbnailPhoto');
+		foreach($attributes as $attribute) {
+			$result = $this->access->readAttribute($this->dn, $attribute);
+			if($result !== false && is_array($result) && isset($result[0])) {
+				$this->avatarImage = $result[0];
+				break;
+			}
+		}
+
+		return $this->avatarImage;
+	}
+
+	/**
+	 * @brief marks the user as having logged in at least once
+	 * @return null
+	 */
+	public function markLogin() {
+		$this->config->setUserValue(
+			$this->uid, 'user_ldap', self::USER_PREFKEY_FIRSTLOGIN, 1);
+	}
+
+	/**
+	 * @brief marks the time when user features like email have been updated
+	 * @return null
+	 */
+	private function markRefreshTime() {
+		$this->config->setUserValue(
+			$this->uid, 'user_ldap', self::USER_PREFKEY_LASTREFRESH, time());
+	}
+
+	/**
+	 * @brief checks whether user features needs to be updated again by
+	 * comparing the difference of time of the last refresh to now with the
+	 * desired interval
+	 * @return bool
+	 */
+	private function needsRefresh() {
+		$lastChecked = $this->config->getUserValue($this->uid, 'user_ldap',
+			self::USER_PREFKEY_LASTREFRESH, 0);
+
+		//TODO make interval configurable
+		if((time() - intval($lastChecked)) < 86400 ) {
+			return false;
+		}
+		return  true;
+	}
+
+	/**
+	 * @brief checks whether an update method specified by feature was run
+	 * already. If not, it will marked like this, because it is expected that
+	 * the method will be run, when false is returned.
+	 * @param string email | quota | avatar (can be extended)
+	 * @return bool
+	 */
+	private function wasRefreshed($feature) {
+		if(isset($this->refreshedFeatures[$feature])) {
+			return true;
+		}
+		$this->refreshedFeatures[$feature] = 1;
+		return false;
+	}
+
+	/**
+	 * @brief fetches the email from LDAP and stores it as ownCloud user value
+	 * @return null
+	 */
+	public function updateEmail() {
+		if($this->wasRefreshed('email')) {
+			return;
+		}
+
+		$email = null;
+		$emailAttribute = $this->connection->ldapEmailAttribute;
+		if(!empty($emailAttribute)) {
+			$aEmail = $this->access->readAttribute($this->dn, $emailAttribute);
+			if($aEmail && (count($aEmail) > 0)) {
+				$email = $aEmail[0];
+			}
+			if(!is_null($email)) {
+				$this->config->setUserValue(
+					$this->uid, 'settings', 'email', $email);
+			}
+		}
+	}
+
+	/**
+	 * @brief fetches the quota from LDAP and stores it as ownCloud user value
+	 * @return null
+	 */
+	public function updateQuota() {
+		if($this->wasRefreshed('quota')) {
+			return;
+		}
+
+		$quota = null;
+		$quotaDefault = $this->connection->ldapQuotaDefault;
+		$quotaAttribute = $this->connection->ldapQuotaAttribute;
+		if(!empty($quotaDefault)) {
+			$quota = $quotaDefault;
+		}
+		if(!empty($quotaAttribute)) {
+			$aQuota = $this->access->readAttribute($this->dn, $quotaAttribute);
+
+			if($aQuota && (count($aQuota) > 0)) {
+				$quota = $aQuota[0];
+			}
+		}
+		if(!is_null($quota)) {
+			$this->config->setUserValue($this->uid, 'files', 'quota', $quota);
+		}
+	}
+
+	/**
+	 * @brief attempts to get an image from LDAP and sets it as ownCloud avatar
+	 * @return null
+	 */
+	public function updateAvatar() {
+		if($this->wasRefreshed('avatar')) {
+			return;
+		}
+		$avatarImage = $this->getAvatarImage();
+		if($avatarImage === false) {
+			//not set, nothing left to do;
+			return;
+		}
+		$this->image->loadFromBase64(base64_encode($avatarImage));
+		$this->setOwnCloudAvatar();
+	}
+
+	/**
+	 * @brief sets an image as ownCloud avatar
+	 * @return null
+	 */
+	private function setOwnCloudAvatar() {
+		if(!$this->image->valid()) {
+			$this->log->log('user_ldap', 'jpegPhoto data invalid for '.$this->dn,
+				\OCP\Util::ERROR);
+			return;
+		}
+		//make sure it is a square and not bigger than 128x128
+		$size = min(array($this->image->width(), $this->image->height(), 128));
+		if(!$this->image->centerCrop($size)) {
+			$this->log->log('user_ldap',
+				'croping image for avatar failed for '.$this->dn,
+				\OCP\Util::ERROR);
+			return;
+		}
+
+		if(!$this->fs->isLoaded()) {
+			$this->fs->setup($this->uid);
+		}
+
+		$avatar = $this->avatarManager->getAvatar($this->uid);
+		$avatar->set($this->image);
+	}
+
+}
diff --git a/apps/user_ldap/tests/access.php b/apps/user_ldap/tests/access.php
index 9beb2b973365a051b0436dbeb4d05358049d7cc4..8ead5d684821a4b3429185495736308512fae257 100644
--- a/apps/user_ldap/tests/access.php
+++ b/apps/user_ldap/tests/access.php
@@ -30,30 +30,39 @@ class Test_Access extends \PHPUnit_Framework_TestCase {
 	private function getConnecterAndLdapMock() {
 		static $conMethods;
 		static $accMethods;
+		static $umMethods;
 
 		if(is_null($conMethods) || is_null($accMethods)) {
 			$conMethods = get_class_methods('\OCA\user_ldap\lib\Connection');
 			$accMethods = get_class_methods('\OCA\user_ldap\lib\Access');
+			$umMethods  = get_class_methods('\OCA\user_ldap\lib\user\Manager');
 		}
 		$lw  = $this->getMock('\OCA\user_ldap\lib\ILDAPWrapper');
 		$connector = $this->getMock('\OCA\user_ldap\lib\Connection',
 									$conMethods,
 									array($lw, null, null));
+		$um = $this->getMock('\OCA\user_ldap\lib\user\Manager',
+			$umMethods, array(
+				$this->getMock('\OCP\IConfig'),
+				$this->getMock('\OCA\user_ldap\lib\FilesystemHelper'),
+				$this->getMock('\OCA\user_ldap\lib\LogWrapper'),
+				$this->getMock('\OCP\IAvatarManager'),
+				$this->getMock('\OCP\Image')));
 
-		return array($lw, $connector);
+		return array($lw, $connector, $um);
 	}
 
 	public function testEscapeFilterPartValidChars() {
-		list($lw, $con) = $this->getConnecterAndLdapMock();
-		$access = new Access($con, $lw);
+		list($lw, $con, $um) = $this->getConnecterAndLdapMock();
+		$access = new Access($con, $lw, $um);
 
 		$input = 'okay';
 		$this->assertTrue($input === $access->escapeFilterPart($input));
 	}
 
 	public function testEscapeFilterPartEscapeWildcard() {
-		list($lw, $con) = $this->getConnecterAndLdapMock();
-		$access = new Access($con, $lw);
+		list($lw, $con, $um) = $this->getConnecterAndLdapMock();
+		$access = new Access($con, $lw, $um);
 
 		$input = '*';
 		$expected = '\\\\*';
@@ -61,8 +70,8 @@ class Test_Access extends \PHPUnit_Framework_TestCase {
 	}
 
 	public function testEscapeFilterPartEscapeWildcard2() {
-		list($lw, $con) = $this->getConnecterAndLdapMock();
-		$access = new Access($con, $lw);
+		list($lw, $con, $um) = $this->getConnecterAndLdapMock();
+		$access = new Access($con, $lw, $um);
 
 		$input = 'foo*bar';
 		$expected = 'foo\\\\*bar';
diff --git a/apps/user_ldap/tests/group_ldap.php b/apps/user_ldap/tests/group_ldap.php
index ecbd42319e319706f791fde97bcca59c4d0b7ddf..1184fe1e82ecf6d562a9a89412de9612249a7a9a 100644
--- a/apps/user_ldap/tests/group_ldap.php
+++ b/apps/user_ldap/tests/group_ldap.php
@@ -42,9 +42,16 @@ class Test_Group_Ldap extends \PHPUnit_Framework_TestCase {
 		$connector = $this->getMock('\OCA\user_ldap\lib\Connection',
 									$conMethods,
 									array($lw, null, null));
+		$um = new \OCA\user_ldap\lib\user\Manager(
+				$this->getMock('\OCP\IConfig'),
+				$this->getMock('\OCA\user_ldap\lib\FilesystemHelper'),
+				$this->getMock('\OCA\user_ldap\lib\LogWrapper'),
+				$this->getMock('\OCP\IAvatarManager'),
+				$this->getMock('\OCP\Image')
+			);
 		$access = $this->getMock('\OCA\user_ldap\lib\Access',
 								 $accMethods,
-								 array($connector, $lw));
+								 array($connector, $lw, $um));
 
 		return $access;
 	}
@@ -112,4 +119,4 @@ class Test_Group_Ldap extends \PHPUnit_Framework_TestCase {
 		$this->assertSame(2, $users);
 	}
 
-}
\ No newline at end of file
+}
diff --git a/apps/user_ldap/tests/user/user.php b/apps/user_ldap/tests/user/user.php
new file mode 100644
index 0000000000000000000000000000000000000000..b66a92372667ff2eab0e8427a563304b78f63447
--- /dev/null
+++ b/apps/user_ldap/tests/user/user.php
@@ -0,0 +1,680 @@
+<?php
+/**
+* ownCloud
+*
+* @author Arthur Schiwon
+* @copyright 2014 Arthur Schiwon blizzz@owncloud.com
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+* License as published by the Free Software Foundation; either
+* version 3 of the License, or any later version.
+*
+* This library 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 library.  If not, see <http://www.gnu.org/licenses/>.
+*
+*/
+
+namespace OCA\user_ldap\tests;
+
+use OCA\user_ldap\lib\user\User;
+
+class Test_User_User extends \PHPUnit_Framework_TestCase {
+
+	private function getTestInstances() {
+		$access  = $this->getMock('\OCA\user_ldap\lib\user\IUserTools');
+		$config  = $this->getMock('\OCP\IConfig');
+		$filesys = $this->getMock('\OCA\user_ldap\lib\FilesystemHelper');
+		$log     = $this->getMock('\OCA\user_ldap\lib\LogWrapper');
+		$avaMgr  = $this->getMock('\OCP\IAvatarManager');
+		$image   = $this->getMock('\OCP\Image');
+
+		return array($access, $config, $filesys, $image, $log, $avaMgr);
+	}
+
+	private function getAdvancedMocks($cfMock, $fsMock, $logMock, $avaMgr) {
+		static $conMethods;
+		static $accMethods;
+		static $umMethods;
+
+		if(is_null($conMethods) || is_null($accMethods)) {
+			$conMethods = get_class_methods('\OCA\user_ldap\lib\Connection');
+			$accMethods = get_class_methods('\OCA\user_ldap\lib\Access');
+			//getConnection shall not be replaced
+			unset($accMethods[array_search('getConnection', $accMethods)]);
+			$umMethods = get_class_methods('\OCA\user_ldap\lib\user\Manager');
+		}
+		$lw = $this->getMock('\OCA\user_ldap\lib\ILDAPWrapper');
+		$im = $this->getMock('\OCP\Image');
+		$um = $this->getMock('\OCA\user_ldap\lib\user\Manager',
+			$umMethods, array($cfMock, $fsMock, $logMock, $avaMgr, $im));
+		$connector = $this->getMock('\OCA\user_ldap\lib\Connection',
+			$conMethods, array($lw, null, null));
+		$access = $this->getMock('\OCA\user_ldap\lib\Access',
+			$accMethods, array($connector, $lw, $um));
+
+		return array($access, $connector);
+	}
+
+	public function testGetDNandUsername() {
+		list($access, $config, $filesys, $image, $log, $avaMgr) =
+			$this->getTestInstances();
+
+		$uid = 'alice';
+		$dn  = 'uid=alice,dc=foo,dc=bar';
+
+		$user = new User(
+			$uid, $dn, $access, $config, $filesys, $image, $log, $avaMgr);
+
+		$this->assertSame($dn, $user->getDN());
+		$this->assertSame($uid, $user->getUsername());
+	}
+
+	public function testUpdateEmailProvided() {
+		list($access, $config, $filesys, $image, $log, $avaMgr) =
+			$this->getTestInstances();
+
+		list($access, $connection) =
+			$this->getAdvancedMocks($config, $filesys, $log, $avaMgr);
+
+		$connection->expects($this->once())
+			->method('__get')
+			->with($this->equalTo('ldapEmailAttribute'))
+			->will($this->returnValue('email'));
+
+		$access->expects($this->once())
+			->method('readAttribute')
+			->with($this->equalTo('uid=alice,dc=foo,dc=bar'),
+				$this->equalTo('email'))
+			->will($this->returnValue(array('alice@foo.bar')));
+
+		$config->expects($this->once())
+			->method('setUserValue')
+			->with($this->equalTo('alice'), $this->equalTo('settings'),
+				$this->equalTo('email'),
+				$this->equalTo('alice@foo.bar'))
+			->will($this->returnValue(true));
+
+		$uid = 'alice';
+		$dn  = 'uid=alice,dc=foo,dc=bar';
+
+		$user = new User(
+			$uid, $dn, $access, $config, $filesys, $image, $log, $avaMgr);
+
+		$user->updateEmail();
+	}
+
+	public function testUpdateEmailNotProvided() {
+		list($access, $config, $filesys, $image, $log, $avaMgr) =
+			$this->getTestInstances();
+
+		list($access, $connection) =
+			$this->getAdvancedMocks($config, $filesys, $log, $avaMgr);
+
+		$connection->expects($this->once())
+			->method('__get')
+			->with($this->equalTo('ldapEmailAttribute'))
+			->will($this->returnValue('email'));
+
+		$access->expects($this->once())
+			->method('readAttribute')
+			->with($this->equalTo('uid=alice,dc=foo,dc=bar'),
+				$this->equalTo('email'))
+			->will($this->returnValue(false));
+
+		$config->expects($this->never())
+			->method('setUserValue');
+
+		$uid = 'alice';
+		$dn  = 'uid=alice,dc=foo,dc=bar';
+
+		$user = new User(
+			$uid, $dn, $access, $config, $filesys, $image, $log, $avaMgr);
+
+		$user->updateEmail();
+	}
+
+	public function testUpdateEmailNotConfigured() {
+		list($access, $config, $filesys, $image, $log, $avaMgr) =
+			$this->getTestInstances();
+
+		list($access, $connection) =
+			$this->getAdvancedMocks($config, $filesys, $log, $avaMgr);
+
+		$connection->expects($this->once())
+			->method('__get')
+			->with($this->equalTo('ldapEmailAttribute'))
+			->will($this->returnValue(''));
+
+		$access->expects($this->never())
+			->method('readAttribute');
+
+		$config->expects($this->never())
+			->method('setUserValue');
+
+		$uid = 'alice';
+		$dn  = 'uid=alice,dc=foo,dc=bar';
+
+		$user = new User(
+			$uid, $dn, $access, $config, $filesys, $image, $log, $avaMgr);
+
+		$user->updateEmail();
+	}
+
+	public function testUpdateQuotaAllProvided() {
+		list($access, $config, $filesys, $image, $log, $avaMgr) =
+			$this->getTestInstances();
+
+		list($access, $connection) =
+			$this->getAdvancedMocks($config, $filesys, $log, $avaMgr);
+
+		$connection->expects($this->at(0))
+			->method('__get')
+			->with($this->equalTo('ldapQuotaDefault'))
+			->will($this->returnValue('23 GB'));
+
+		$connection->expects($this->at(1))
+			->method('__get')
+			->with($this->equalTo('ldapQuotaAttribute'))
+			->will($this->returnValue('myquota'));
+
+		$connection->expects($this->exactly(2))
+			->method('__get');
+
+		$access->expects($this->once())
+			->method('readAttribute')
+			->with($this->equalTo('uid=alice,dc=foo,dc=bar'),
+				$this->equalTo('myquota'))
+			->will($this->returnValue(array('42 GB')));
+
+		$config->expects($this->once())
+			->method('setUserValue')
+			->with($this->equalTo('alice'),
+				$this->equalTo('files'),
+				$this->equalTo('quota'),
+				$this->equalTo('42 GB'))
+			->will($this->returnValue(true));
+
+		$uid = 'alice';
+		$dn  = 'uid=alice,dc=foo,dc=bar';
+
+		$user = new User(
+			$uid, $dn, $access, $config, $filesys, $image, $log, $avaMgr);
+
+		$user->updateQuota();
+	}
+
+	public function testUpdateQuotaDefaultProvided() {
+		list($access, $config, $filesys, $image, $log, $avaMgr) =
+			$this->getTestInstances();
+
+		list($access, $connection) =
+			$this->getAdvancedMocks($config, $filesys, $log, $avaMgr);
+
+		$connection->expects($this->at(0))
+			->method('__get')
+			->with($this->equalTo('ldapQuotaDefault'))
+			->will($this->returnValue('23 GB'));
+
+		$connection->expects($this->at(1))
+			->method('__get')
+			->with($this->equalTo('ldapQuotaAttribute'))
+			->will($this->returnValue('myquota'));
+
+		$connection->expects($this->exactly(2))
+			->method('__get');
+
+		$access->expects($this->once())
+			->method('readAttribute')
+			->with($this->equalTo('uid=alice,dc=foo,dc=bar'),
+				$this->equalTo('myquota'))
+			->will($this->returnValue(false));
+
+		$config->expects($this->once())
+			->method('setUserValue')
+			->with($this->equalTo('alice'),
+				$this->equalTo('files'),
+				$this->equalTo('quota'),
+				$this->equalTo('23 GB'))
+			->will($this->returnValue(true));
+
+		$uid = 'alice';
+		$dn  = 'uid=alice,dc=foo,dc=bar';
+
+		$user = new User(
+			$uid, $dn, $access, $config, $filesys, $image, $log, $avaMgr);
+
+		$user->updateQuota();
+	}
+
+	public function testUpdateQuotaIndividualProvided() {
+		list($access, $config, $filesys, $image, $log, $avaMgr) =
+			$this->getTestInstances();
+
+		list($access, $connection) =
+			$this->getAdvancedMocks($config, $filesys, $log, $avaMgr);
+
+		$connection->expects($this->at(0))
+			->method('__get')
+			->with($this->equalTo('ldapQuotaDefault'))
+			->will($this->returnValue(''));
+
+		$connection->expects($this->at(1))
+			->method('__get')
+			->with($this->equalTo('ldapQuotaAttribute'))
+			->will($this->returnValue('myquota'));
+
+		$connection->expects($this->exactly(2))
+			->method('__get');
+
+		$access->expects($this->once())
+			->method('readAttribute')
+			->with($this->equalTo('uid=alice,dc=foo,dc=bar'),
+				$this->equalTo('myquota'))
+			->will($this->returnValue(array('23 GB')));
+
+		$config->expects($this->once())
+			->method('setUserValue')
+			->with($this->equalTo('alice'),
+				$this->equalTo('files'),
+				$this->equalTo('quota'),
+				$this->equalTo('23 GB'))
+			->will($this->returnValue(true));
+
+		$uid = 'alice';
+		$dn  = 'uid=alice,dc=foo,dc=bar';
+
+		$user = new User(
+			$uid, $dn, $access, $config, $filesys, $image, $log, $avaMgr);
+
+		$user->updateQuota();
+	}
+
+	public function testUpdateQuotaNoneProvided() {
+		list($access, $config, $filesys, $image, $log, $avaMgr) =
+			$this->getTestInstances();
+
+		list($access, $connection) =
+			$this->getAdvancedMocks($config, $filesys, $log, $avaMgr);
+
+		$connection->expects($this->at(0))
+			->method('__get')
+			->with($this->equalTo('ldapQuotaDefault'))
+			->will($this->returnValue(''));
+
+		$connection->expects($this->at(1))
+			->method('__get')
+			->with($this->equalTo('ldapQuotaAttribute'))
+			->will($this->returnValue('myquota'));
+
+		$connection->expects($this->exactly(2))
+			->method('__get');
+
+		$access->expects($this->once())
+			->method('readAttribute')
+			->with($this->equalTo('uid=alice,dc=foo,dc=bar'),
+				$this->equalTo('myquota'))
+			->will($this->returnValue(false));
+
+		$config->expects($this->never())
+			->method('setUserValue');
+
+		$uid = 'alice';
+		$dn  = 'uid=alice,dc=foo,dc=bar';
+
+		$user = new User(
+			$uid, $dn, $access, $config, $filesys, $image, $log, $avaMgr);
+
+		$user->updateQuota();
+	}
+
+	public function testUpdateQuotaNoneConfigured() {
+		list($access, $config, $filesys, $image, $log, $avaMgr) =
+			$this->getTestInstances();
+
+		list($access, $connection) =
+			$this->getAdvancedMocks($config, $filesys, $log, $avaMgr);
+
+		$connection->expects($this->at(0))
+			->method('__get')
+			->with($this->equalTo('ldapQuotaDefault'))
+			->will($this->returnValue(''));
+
+		$connection->expects($this->at(1))
+			->method('__get')
+			->with($this->equalTo('ldapQuotaAttribute'))
+			->will($this->returnValue(''));
+
+		$connection->expects($this->exactly(2))
+			->method('__get');
+
+		$access->expects($this->never())
+			->method('readAttribute');
+
+		$config->expects($this->never())
+			->method('setUserValue');
+
+		$uid = 'alice';
+		$dn  = 'uid=alice,dc=foo,dc=bar';
+
+		$user = new User(
+			$uid, $dn, $access, $config, $filesys, $image, $log, $avaMgr);
+
+		$user->updateQuota();
+	}
+
+	//the testUpdateAvatar series also implicitely tests getAvatarImage
+	public function testUpdateAvatarJpegPhotoProvided() {
+		list($access, $config, $filesys, $image, $log, $avaMgr) =
+			$this->getTestInstances();
+
+		list($access, $connection) =
+			$this->getAdvancedMocks($config, $filesys, $log, $avaMgr);
+
+		$access->expects($this->once())
+			->method('readAttribute')
+			->with($this->equalTo('uid=alice,dc=foo,dc=bar'),
+				$this->equalTo('jpegPhoto'))
+			->will($this->returnValue(array('this is a photo')));
+
+		$image->expects($this->once())
+			->method('valid')
+			->will($this->returnValue(true));
+		$image->expects($this->once())
+			->method('width')
+			->will($this->returnValue(128));
+		$image->expects($this->once())
+			->method('height')
+			->will($this->returnValue(128));
+		$image->expects($this->once())
+			->method('centerCrop')
+			->will($this->returnValue(true));
+
+		$filesys->expects($this->once())
+			->method('isLoaded')
+			->will($this->returnValue(true));
+
+		$avatar = $this->getMock('\OCP\IAvatar');
+		$avatar->expects($this->once())
+			->method('set')
+			->with($this->isInstanceOf($image));
+
+		$avaMgr->expects($this->once())
+			->method('getAvatar')
+			->with($this->equalTo('alice'))
+			->will($this->returnValue($avatar));
+
+		$uid = 'alice';
+		$dn  = 'uid=alice,dc=foo,dc=bar';
+
+		$user = new User(
+			$uid, $dn, $access, $config, $filesys, $image, $log, $avaMgr);
+
+		$user->updateAvatar();
+	}
+
+	public function testUpdateAvatarThumbnailPhotoProvided() {
+		list($access, $config, $filesys, $image, $log, $avaMgr) =
+			$this->getTestInstances();
+
+		list($access, $connection) =
+			$this->getAdvancedMocks($config, $filesys, $log, $avaMgr);
+
+		$access->expects($this->at(0))
+			->method('readAttribute')
+			->with($this->equalTo('uid=alice,dc=foo,dc=bar'),
+				$this->equalTo('jpegPhoto'))
+			->will($this->returnValue(false));
+
+		$access->expects($this->at(1))
+			->method('readAttribute')
+			->with($this->equalTo('uid=alice,dc=foo,dc=bar'),
+				$this->equalTo('thumbnailPhoto'))
+			->will($this->returnValue(array('this is a photo')));
+
+		$access->expects($this->exactly(2))
+			->method('readAttribute');
+
+		$image->expects($this->once())
+			->method('valid')
+			->will($this->returnValue(true));
+		$image->expects($this->once())
+			->method('width')
+			->will($this->returnValue(128));
+		$image->expects($this->once())
+			->method('height')
+			->will($this->returnValue(128));
+		$image->expects($this->once())
+			->method('centerCrop')
+			->will($this->returnValue(true));
+
+		$filesys->expects($this->once())
+			->method('isLoaded')
+			->will($this->returnValue(true));
+
+		$avatar = $this->getMock('\OCP\IAvatar');
+		$avatar->expects($this->once())
+			->method('set')
+			->with($this->isInstanceOf($image));
+
+		$avaMgr->expects($this->once())
+			->method('getAvatar')
+			->with($this->equalTo('alice'))
+			->will($this->returnValue($avatar));
+
+		$uid = 'alice';
+		$dn  = 'uid=alice,dc=foo,dc=bar';
+
+		$user = new User(
+			$uid, $dn, $access, $config, $filesys, $image, $log, $avaMgr);
+
+		$user->updateAvatar();
+	}
+
+	public function testUpdateAvatarNotProvided() {
+		list($access, $config, $filesys, $image, $log, $avaMgr) =
+			$this->getTestInstances();
+
+		list($access, $connection) =
+			$this->getAdvancedMocks($config, $filesys, $log, $avaMgr);
+
+		$access->expects($this->at(0))
+			->method('readAttribute')
+			->with($this->equalTo('uid=alice,dc=foo,dc=bar'),
+				$this->equalTo('jpegPhoto'))
+			->will($this->returnValue(false));
+
+		$access->expects($this->at(1))
+			->method('readAttribute')
+			->with($this->equalTo('uid=alice,dc=foo,dc=bar'),
+				$this->equalTo('thumbnailPhoto'))
+			->will($this->returnValue(false));
+
+		$access->expects($this->exactly(2))
+			->method('readAttribute');
+
+		$image->expects($this->never())
+			->method('valid');
+		$image->expects($this->never())
+			->method('width');
+		$image->expects($this->never())
+			->method('height');
+		$image->expects($this->never())
+			->method('centerCrop');
+
+		$filesys->expects($this->never())
+			->method('isLoaded');
+
+		$avaMgr->expects($this->never())
+			->method('getAvatar');
+
+		$uid = 'alice';
+		$dn  = 'uid=alice,dc=foo,dc=bar';
+
+		$user = new User(
+			$uid, $dn, $access, $config, $filesys, $image, $log, $avaMgr);
+
+		$user->updateAvatar();
+	}
+
+	public function testUpdateBeforeFirstLogin() {
+		list($access, $config, $filesys, $image, $log, $avaMgr) =
+			$this->getTestInstances();
+
+		list($access, $connection) =
+			$this->getAdvancedMocks($config, $filesys, $log, $avaMgr);
+
+		$config->expects($this->at(0))
+			->method('getUserValue')
+			->with($this->equalTo('alice'), $this->equalTo('user_ldap'),
+				$this->equalTo(User::USER_PREFKEY_FIRSTLOGIN),
+				$this->equalTo(0))
+			->will($this->returnValue(0));
+
+		$config->expects($this->at(1))
+			->method('getUserValue')
+			->with($this->equalTo('alice'), $this->equalTo('user_ldap'),
+				$this->equalTo(User::USER_PREFKEY_LASTREFRESH),
+				$this->equalTo(0))
+			->will($this->returnValue(0));
+
+		$config->expects($this->exactly(2))
+			->method('getUserValue');
+
+		$config->expects($this->never())
+			->method('setUserValue');
+
+		$uid = 'alice';
+		$dn  = 'uid=alice,dc=foo,dc=bar';
+
+		$user = new User(
+			$uid, $dn, $access, $config, $filesys, $image, $log, $avaMgr);
+
+		$user->update();
+	}
+
+	public function testUpdateAfterFirstLogin() {
+		list($access, $config, $filesys, $image, $log, $avaMgr) =
+			$this->getTestInstances();
+
+		list($access, $connection) =
+			$this->getAdvancedMocks($config, $filesys, $log, $avaMgr);
+
+		$config->expects($this->at(0))
+			->method('getUserValue')
+			->with($this->equalTo('alice'), $this->equalTo('user_ldap'),
+				$this->equalTo(User::USER_PREFKEY_FIRSTLOGIN),
+				$this->equalTo(0))
+			->will($this->returnValue(1));
+
+		$config->expects($this->at(1))
+			->method('getUserValue')
+			->with($this->equalTo('alice'), $this->equalTo('user_ldap'),
+				$this->equalTo(User::USER_PREFKEY_LASTREFRESH),
+				$this->equalTo(0))
+			->will($this->returnValue(0));
+
+		$config->expects($this->exactly(2))
+			->method('getUserValue');
+
+		$config->expects($this->once())
+			->method('setUserValue')
+			->with($this->equalTo('alice'), $this->equalTo('user_ldap'),
+				$this->equalTo(User::USER_PREFKEY_LASTREFRESH),
+				$this->anything())
+			->will($this->returnValue(true));
+
+		$uid = 'alice';
+		$dn  = 'uid=alice,dc=foo,dc=bar';
+
+		$user = new User(
+			$uid, $dn, $access, $config, $filesys, $image, $log, $avaMgr);
+
+		$user->update();
+	}
+
+	public function testUpdateNoRefresh() {
+		list($access, $config, $filesys, $image, $log, $avaMgr) =
+			$this->getTestInstances();
+
+		list($access, $connection) =
+			$this->getAdvancedMocks($config, $filesys, $log, $avaMgr);
+
+		$config->expects($this->at(0))
+			->method('getUserValue')
+			->with($this->equalTo('alice'), $this->equalTo('user_ldap'),
+				$this->equalTo(User::USER_PREFKEY_FIRSTLOGIN),
+				$this->equalTo(0))
+			->will($this->returnValue(1));
+
+		$config->expects($this->at(1))
+			->method('getUserValue')
+			->with($this->equalTo('alice'), $this->equalTo('user_ldap'),
+				$this->equalTo(User::USER_PREFKEY_LASTREFRESH),
+				$this->equalTo(0))
+			->will($this->returnValue(time()));
+
+		$config->expects($this->exactly(2))
+			->method('getUserValue');
+
+		$config->expects($this->never())
+			->method('setUserValue');
+
+		$uid = 'alice';
+		$dn  = 'uid=alice,dc=foo,dc=bar';
+
+		$user = new User(
+			$uid, $dn, $access, $config, $filesys, $image, $log, $avaMgr);
+
+		$user->update();
+	}
+
+	public function testMarkLogin() {
+		list($access, $config, $filesys, $image, $log, $avaMgr) =
+			$this->getTestInstances();
+
+		$config->expects($this->once())
+			->method('setUserValue')
+			->with($this->equalTo('alice'),
+				$this->equalTo('user_ldap'),
+				$this->equalTo(User::USER_PREFKEY_FIRSTLOGIN),
+				$this->equalTo(1))
+			->will($this->returnValue(true));
+
+		$uid = 'alice';
+		$dn  = 'uid=alice,dc=foo,dc=bar';
+
+		$user = new User(
+			$uid, $dn, $access, $config, $filesys, $image, $log, $avaMgr);
+
+		$user->markLogin();
+	}
+
+	public function testGetAvatarImageProvided() {
+		list($access, $config, $filesys, $image, $log, $avaMgr) =
+			$this->getTestInstances();
+
+		$access->expects($this->once())
+			->method('readAttribute')
+			->with($this->equalTo('uid=alice,dc=foo,dc=bar'),
+				$this->equalTo('jpegPhoto'))
+			->will($this->returnValue(array('this is a photo')));
+
+		$uid = 'alice';
+		$dn  = 'uid=alice,dc=foo,dc=bar';
+
+		$user = new User(
+			$uid, $dn, $access, $config, $filesys, $image, $log, $avaMgr);
+
+		$photo = $user->getAvatarImage();
+		$this->assertSame('this is a photo', $photo);
+		//make sure readAttribute is not called again but the already fetched
+		//photo is returned
+		$photo = $user->getAvatarImage();
+	}
+}
diff --git a/apps/user_ldap/tests/user_ldap.php b/apps/user_ldap/tests/user_ldap.php
index 2535c34bcf5fa2604ae555daf78ee61da5dbeefa..8787e023655933bdfe2ebdc40c266a3f66856e49 100644
--- a/apps/user_ldap/tests/user_ldap.php
+++ b/apps/user_ldap/tests/user_ldap.php
@@ -29,6 +29,7 @@ use \OCA\user_ldap\lib\ILDAPWrapper;
 
 class Test_User_Ldap_Direct extends \PHPUnit_Framework_TestCase {
 	protected $backend;
+	protected $access;
 
 	public function setUp() {
 		\OC_User::clearBackends();
@@ -38,18 +39,35 @@ class Test_User_Ldap_Direct extends \PHPUnit_Framework_TestCase {
 	private function getAccessMock() {
 		static $conMethods;
 		static $accMethods;
+		static $uMethods;
 
 		if(is_null($conMethods) || is_null($accMethods)) {
 			$conMethods = get_class_methods('\OCA\user_ldap\lib\Connection');
 			$accMethods = get_class_methods('\OCA\user_ldap\lib\Access');
+			unset($accMethods[array_search('getConnection', $accMethods)]);
+			$uMethods   = get_class_methods('\OCA\user_ldap\lib\user\User');
+			unset($uMethods[array_search('getUsername', $uMethods)]);
+			unset($uMethods[array_search('getDN', $uMethods)]);
+			unset($uMethods[array_search('__construct', $uMethods)]);
 		}
 		$lw  = $this->getMock('\OCA\user_ldap\lib\ILDAPWrapper');
 		$connector = $this->getMock('\OCA\user_ldap\lib\Connection',
 									$conMethods,
 									array($lw, null, null));
+
+		$um = new \OCA\user_ldap\lib\user\Manager(
+				$this->getMock('\OCP\IConfig'),
+				$this->getMock('\OCA\user_ldap\lib\FilesystemHelper'),
+				$this->getMock('\OCA\user_ldap\lib\LogWrapper'),
+				$this->getMock('\OCP\IAvatarManager'),
+				$this->getMock('\OCP\Image')
+			);
+
 		$access = $this->getMock('\OCA\user_ldap\lib\Access',
 								 $accMethods,
-								 array($connector, $lw));
+								 array($connector, $lw, $um));
+
+		$um->setLdapAccess($access);
 
 		return $access;
 	}
@@ -60,16 +78,16 @@ class Test_User_Ldap_Direct extends \PHPUnit_Framework_TestCase {
 			   ->will($this->returnCallback(function($uid) {
 					switch ($uid) {
 						case 'gunslinger':
-							return 'dnOfRoland';
+							return 'dnOfRoland,dc=test';
 							break;
 						case 'formerUser':
-							return 'dnOfFormerUser';
+							return 'dnOfFormerUser,dc=test';
 							break;
 						case 'newyorker':
-							return 'dnOfNewYorker';
+							return 'dnOfNewYorker,dc=test';
 							break;
 						case 'ladyofshadows':
-							return 'dnOfLadyOfShadows';
+							return 'dnOfLadyOfShadows,dc=test';
 							break;
 						default:
 							return false;
@@ -102,14 +120,14 @@ class Test_User_Ldap_Direct extends \PHPUnit_Framework_TestCase {
 			   ->method('fetchListOfUsers')
 			   ->will($this->returnCallback(function($filter) {
 					if($filter === 'roland') {
-						return array('dnOfRoland');
+						return array('dnOfRoland,dc=test');
 					}
 					return array();
 			   }));
 
 		$access->expects($this->any())
 			   ->method('dn2username')
-			   ->with($this->equalTo('dnOfRoland'))
+			   ->with($this->equalTo('dnOfRoland,dc=test'))
 			   ->will($this->returnValue('gunslinger'));
 
 		$access->expects($this->any())
@@ -337,7 +355,7 @@ class Test_User_Ldap_Direct extends \PHPUnit_Framework_TestCase {
 		$access->expects($this->any())
 			   ->method('readAttribute')
 			   ->will($this->returnCallback(function($dn) {
-					if($dn === 'dnOfRoland') {
+					if($dn === 'dnOfRoland,dc=test') {
 						return array();
 					}
 					return false;
@@ -365,7 +383,7 @@ class Test_User_Ldap_Direct extends \PHPUnit_Framework_TestCase {
 		$access->expects($this->any())
 			   ->method('readAttribute')
 			   ->will($this->returnCallback(function($dn) {
-					if($dn === 'dnOfRoland') {
+					if($dn === 'dnOfRoland,dc=test') {
 						return array();
 					}
 					return false;
@@ -411,13 +429,13 @@ class Test_User_Ldap_Direct extends \PHPUnit_Framework_TestCase {
 			   ->method('readAttribute')
 			   ->will($this->returnCallback(function($dn, $attr) {
 					switch ($dn) {
-						case 'dnOfRoland':
+						case 'dnOfRoland,dc=test':
 							if($attr === 'testAttribute') {
 								return array('/tmp/rolandshome/');
 							}
 							return array();
 							break;
-						case 'dnOfLadyOfShadows':
+						case 'dnOfLadyOfShadows,dc=test':
 							if($attr === 'testAttribute') {
 								return array('susannah/');
 							}
@@ -457,7 +475,7 @@ class Test_User_Ldap_Direct extends \PHPUnit_Framework_TestCase {
 			   ->method('readAttribute')
 			   ->will($this->returnCallback(function($dn, $attr) {
 					switch ($dn) {
-						case 'dnOfRoland':
+						case 'dnOfRoland,dc=test':
 							if($attr === 'displayname') {
 								return array('Roland Deschain');
 							}
diff --git a/apps/user_ldap/user_ldap.php b/apps/user_ldap/user_ldap.php
index aece2ee9aa2bfca9c33f9213a925f4f7a440a53c..9fa2a6b418c8a920dea050fa95f9b12ed7d2727e 100644
--- a/apps/user_ldap/user_ldap.php
+++ b/apps/user_ldap/user_ldap.php
@@ -28,128 +28,19 @@ namespace OCA\user_ldap;
 use OCA\user_ldap\lib\BackendUtility;
 
 class USER_LDAP extends BackendUtility implements \OCP\UserInterface {
-
-	private function updateQuota($dn) {
-		$quota = null;
-		$quotaDefault = $this->access->connection->ldapQuotaDefault;
-		$quotaAttribute = $this->access->connection->ldapQuotaAttribute;
-		if(!empty($quotaDefault)) {
-			$quota = $quotaDefault;
-		}
-		if(!empty($quotaAttribute)) {
-			$aQuota = $this->access->readAttribute($dn, $quotaAttribute);
-
-			if($aQuota && (count($aQuota) > 0)) {
-				$quota = $aQuota[0];
-			}
-		}
-		if(!is_null($quota)) {
-			\OCP\Config::setUserValue(	$this->access->dn2username($dn),
-										'files',
-										'quota',
-										\OCP\Util::computerFileSize($quota));
-		}
-	}
-
-	private function updateEmail($dn) {
-		$email = null;
-		$emailAttribute = $this->access->connection->ldapEmailAttribute;
-		if(!empty($emailAttribute)) {
-			$aEmail = $this->access->readAttribute($dn, $emailAttribute);
-			if($aEmail && (count($aEmail) > 0)) {
-				$email = $aEmail[0];
-			}
-			if(!is_null($email)) {
-				\OCP\Config::setUserValue(	$this->access->dn2username($dn),
-											'settings',
-											'email',
-											$email);
-			}
-		}
-	}
-
-	/**
-	 * reads jpegPhoto and set is as avatar if available
-	 * @param string $uid ownCloud user name
-	 * @param string $dn the user's LDAP DN
-	 * @return void
-	 */
-	private function updateAvatar($uid, $dn) {
-		$hasLoggedIn = \OCP\Config::getUserValue($uid, 'user_ldap',
-												 'firstLoginAccomplished', 0);
-		$lastChecked = \OCP\Config::getUserValue($uid, 'user_ldap',
-												 'lastJpegPhotoLookup', 0);
-		if(($hasLoggedIn !== '1') || (time() - intval($lastChecked)) < 86400 ) {
-			//update only once a day
-			return;
-		}
-
-		$avatarImage = $this->getAvatarImage($uid, $dn);
-		if($avatarImage === false) {
-			//not set, nothing left to do;
-			return;
-		}
-
-		$image = new \OCP\Image();
-		$image->loadFromBase64(base64_encode($avatarImage));
-
-		if(!$image->valid()) {
-			\OCP\Util::writeLog('user_ldap', 'jpegPhoto data invalid for '.$dn,
-								\OCP\Util::ERROR);
-			return;
-		}
-		//make sure it is a square and not bigger than 128x128
-		$size = min(array($image->width(), $image->height(), 128));
-		if(!$image->centerCrop($size)) {
-			\OCP\Util::writeLog('user_ldap',
-								'croping image for avatar failed for '.$dn,
-								\OCP\Util::ERROR);
-			return;
-		}
-
-		if(!\OC\Files\Filesystem::$loaded) {
-			\OC_Util::setupFS($uid);
-		}
-
-		$avatarManager = \OC::$server->getAvatarManager();
-		$avatar = $avatarManager->getAvatar($uid);
-		$avatar->set($image);
-	}
-
 	/**
 	 * checks whether the user is allowed to change his avatar in ownCloud
 	 * @param string $uid the ownCloud user name
 	 * @return boolean either the user can or cannot
 	 */
 	public function canChangeAvatar($uid) {
-		$dn = $this->access->username2dn($uid);
-		if(!$dn) {
+		$user = $this->access->userManager->get($uid);
+		if(is_null($user)) {
 			return false;
 		}
-		if($this->getAvatarImage($uid, $dn) === false) {
-			//The user is allowed to change his avatar in ownCloud only if no
-			//avatar is provided by LDAP
+		if($user->getAvatarImage() === false) {
 			return true;
 		}
-		return false;
-	}
-
-	/**
-	 * reads the image from LDAP that shall be used as Avatar
-	 * @param string $uid the ownCloud user name
-	 * @param string $dn the user DN
-	 * @return string data (provided by LDAP) | false
-	 */
-	private function getAvatarImage($uid, $dn) {
-		$attributes = array('jpegPhoto', 'thumbnailPhoto');
-		foreach($attributes as $attribute) {
-			$result = $this->access->readAttribute($dn, $attribute);
-			\OCP\Config::setUserValue($uid, 'user_ldap', 'lastJpegPhotoLookup',
-									  time());
-			if($result !== false && is_array($result) && isset($result[0])) {
-				return $result[0];
-			}
-		}
 
 		return false;
 	}
@@ -174,25 +65,17 @@ class USER_LDAP extends BackendUtility implements \OCP\UserInterface {
 		}
 		$dn = $ldap_users[0];
 
-		//do we have a username for him/her?
-		$ocname = $this->access->dn2username($dn);
-
-		if($ocname) {
-			//update some settings, if necessary
-			$this->updateQuota($dn);
-			$this->updateEmail($dn);
-
+		$user = $this->access->userManager->get($dn);
+		if($user->getUsername() !== false) {
 			//are the credentials OK?
 			if(!$this->access->areCredentialsValid($dn, $password)) {
 				return false;
 			}
 
-			\OCP\Config::setUserValue($ocname, 'user_ldap',
-									  'firstLoginAccomplished', 1);
+			$user->markLogin();
+			$user->update();
 
-			$this->updateAvatar($ocname, $dn);
-			//give back the display name
-			return $ocname;
+			return $user->getUsername();
 		}
 
 		return false;
@@ -249,13 +132,14 @@ class USER_LDAP extends BackendUtility implements \OCP\UserInterface {
 			return $this->access->connection->getFromCache('userExists'.$uid);
 		}
 		//getting dn, if false the user does not exist. If dn, he may be mapped only, requires more checking.
-		$dn = $this->access->username2dn($uid);
-		if(!$dn) {
+		$user = $this->access->userManager->get($uid);
+		if(is_null($user)) {
 			\OCP\Util::writeLog('user_ldap', 'No DN found for '.$uid.' on '.
 				$this->access->connection->ldapHost, \OCP\Util::DEBUG);
 			$this->access->connection->writeToCache('userExists'.$uid, false);
 			return false;
 		}
+		$dn = $user->getDN();
 		//check if user really still exists by reading its entry
 		if(!is_array($this->access->readAttribute($dn, ''))) {
 			\OCP\Util::writeLog('user_ldap', 'LDAP says no user '.$dn.' on '.
@@ -265,8 +149,7 @@ class USER_LDAP extends BackendUtility implements \OCP\UserInterface {
 		}
 
 		$this->access->connection->writeToCache('userExists'.$uid, true);
-		$this->updateQuota($dn);
-		$this->updateAvatar($uid, $dn);
+		$user->update();
 		return true;
 	}