diff --git a/pom.xml b/pom.xml
index 94f8e7d67f11d90e160c99a46ed68e50600c1a7a..f6cb96265368cae373ea520eeaa983182a6effef 100644
--- a/pom.xml
+++ b/pom.xml
@@ -14,6 +14,9 @@
 		<mobile.production.path>../arsnova-mobile/src/main/webapp/build/production/ARSnova</mobile.production.path>
 		<mobile.testing.path>../arsnova-mobile/src/main/webapp/build/testing/ARSnova</mobile.testing.path>
 		<mobile.path>${mobile.production.path}</mobile.path>
+		<customization.path>${basedir}/../arsnova-customization/src/main/webapp</customization.path>
+		<presenter.rootPath>${basedir}/../arsnova-presenter</presenter.rootPath>
+		<presenter.outputDir>target/arsnova-presenter-1.1.0-SNAPSHOT</presenter.outputDir>
 	</properties>
 
 	<developers>
@@ -134,12 +137,6 @@
 	</repositories>
 
 	<dependencies>
-		<dependency>
-			<groupId>de.thm.arsnova</groupId>
-			<artifactId>arsnova-mobile</artifactId>
-			<version>0.0.1-SNAPSHOT</version>
-			<type>war</type>
-		</dependency>
 		<!-- Spring -->
 		<dependency>
 			<groupId>org.springframework</groupId>
@@ -179,6 +176,11 @@
 			<artifactId>spring-security-cas</artifactId>
 			<version>${org.springframework.security-version}</version>
 		</dependency>
+		<dependency>
+			<groupId>org.springframework.security</groupId>
+			<artifactId>spring-security-ldap</artifactId>
+			<version>${org.springframework.security-version}</version>
+		</dependency>
 		<dependency>
 			<groupId>jstl</groupId>
 			<artifactId>jstl</artifactId>
@@ -227,6 +229,11 @@
 			<version>3.0.1</version>
 			<scope>provided</scope>
 		</dependency>
+		<dependency>
+			<groupId>javax.mail</groupId>
+			<artifactId>mail</artifactId>
+			<version>1.4.7</version>
+		</dependency>
 		<dependency>
 			<groupId>org.springframework</groupId>
 			<artifactId>spring-test</artifactId>
@@ -310,6 +317,20 @@
 			<version>0.9.1</version>
 			<scope>test</scope>
 		</dependency>
+		
+		<dependency>
+            <groupId>org.apache.directory.server</groupId>
+            <artifactId>apacheds-core</artifactId>
+            <version>1.5.5</version>
+            <scope>runtime</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.directory.server</groupId>
+            <artifactId>apacheds-server-jndi</artifactId>
+            <version>1.5.5</version>
+            <scope>runtime</scope>
+        </dependency>
+
 	</dependencies>
 	<build>
 		<plugins>
@@ -323,18 +344,32 @@
 				</configuration>
 			</plugin>
 			<plugin>
-				<groupId>org.mortbay.jetty</groupId>
+				<groupId>org.eclipse.jetty</groupId>
 				<artifactId>jetty-maven-plugin</artifactId>
-				<version>7.6.5.v20120716</version>
+				<version>9.2.1.v20140609</version>
 				<configuration>
 					<scanIntervalSeconds>1</scanIntervalSeconds>
-					<webApp> 
-						<contextPath>/</contextPath>
-						<baseResource implementation="org.eclipse.jetty.util.resource.ResourceCollection">
-							<resourcesAsCSV>src/main/webapp,${mobile.path}</resourcesAsCSV> 
-						</baseResource>
+					<webApp>
 						<overrideDescriptor>src/main/webapp/WEB-INF/web-dev.xml</overrideDescriptor>
 					</webApp>
+					<contextHandlers>
+						<contextHandler implementation="org.eclipse.jetty.webapp.WebAppContext">
+							<contextPath>/mobile</contextPath>
+							<resourceBase>${mobile.path}</resourceBase>
+						</contextHandler>
+						<contextHandler implementation="org.eclipse.jetty.webapp.WebAppContext">
+							<contextPath>/presenter</contextPath>
+							<baseResource implementation="org.eclipse.jetty.util.resource.ResourceCollection">
+								<resourcesAsCSV>${presenter.rootPath}/${presenter.outputDir},${presenter.rootPath}/src/main/websources,${presenter.rootPath}/src/main/webapp</resourcesAsCSV>
+							</baseResource>
+							<descriptor>${presenter.rootPath}/src/main/config/WEB-INF/web.dev.xml</descriptor>
+							<aliasCheck implementation="org.eclipse.jetty.server.handler.AllowSymLinkAliasChecker" />
+						</contextHandler>
+						<contextHandler implementation="org.eclipse.jetty.webapp.WebAppContext">
+							<contextPath>/customization</contextPath>
+							<resourceBase>${customization.path}</resourceBase>
+						</contextHandler>
+					</contextHandlers>
 				</configuration>
 			</plugin>
 			<plugin>
@@ -398,7 +433,7 @@
 		</plugins>
 	</build>
 
-	<name>ARSnova</name>
+	<name>ARSnova Backend</name>
 	<description>ARSnova is a great audience response system</description>
 	<organization>
 		<name>Technische Hochschule Mittelhessen</name>
diff --git a/src/main/java/de/thm/arsnova/controller/ConfigurationController.java b/src/main/java/de/thm/arsnova/controller/ConfigurationController.java
new file mode 100644
index 0000000000000000000000000000000000000000..9d4d96c73978e0a84782ce3b1a4b708a804a73bd
--- /dev/null
+++ b/src/main/java/de/thm/arsnova/controller/ConfigurationController.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2014 THM webMedia
+ *
+ * This file is part of ARSnova.
+ *
+ * ARSnova is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * ARSnova 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package de.thm.arsnova.controller;
+
+import java.util.HashMap;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+/**
+ * The ConfigurationController provides frontend clients with information necessary to correctly interact with the 
+ * backend and other frontends as well as settings for ARSnova. The the alternative /arsnova-config route is necessary
+ * in case the backend application is deployed as root context.
+ */
+@Controller
+@RequestMapping({"/configuration", "/arsnova-config"})
+public class ConfigurationController extends AbstractController {
+	@Value("${security.guest.enabled}")
+	private String guestEnabled;
+
+	public static final Logger LOGGER = LoggerFactory
+			.getLogger(ConfigurationController.class);
+
+	@Value("${customization.path}")
+	private String customizationPath;
+
+	@Value("${mobile.path}")
+	private String mobilePath;
+
+	@Value("${presenter.path}")
+	private String presenterPath;
+
+	@Value("${links.overlay.url}")
+	private String overlayUrl;
+
+	@Value("${links.organization.url}")
+	private String organizationUrl;
+
+	@Value("${links.imprint.url}")
+	private String imprintUrl;
+
+	@Value("${links.privacy-policy.url}")
+	private String privacyPolicyUrl;
+
+	@Value("${links.documentation.url}")
+	private String documentationUrl;
+
+	@Value("${features.mathjax.enabled:true}")
+	private String mathJaxEnabled;
+
+	@Value("${features.markdown.enabled:false}")
+	private String markdownEnabled;
+
+	@Value("${features.learning-progress.enabled:false}")
+	private String learningProgressEnabled;
+
+	@Value("${features.question-format.flashcard.enabled:false}")
+	private String flashcardEnabled;
+
+	@Value("${features.question-format.grid-square.enabled:false}")
+	private String gridSquareEnabled;
+
+	@Value("${question.answer-option-limit:8}")
+	private String answerOptionLimit;
+
+	@Value("${question.parse-answer-option-formatting:false}")
+	private String parseAnswerOptionFormatting;
+
+	@RequestMapping(method = RequestMethod.GET)
+	@ResponseBody
+	public final HashMap<String, Object> getConfiguration(HttpServletRequest request) {
+		HashMap<String, Object> config = new HashMap<String, Object>();
+		HashMap<String, Boolean> features = new HashMap<String, Boolean>();
+
+		/* The API path could be unknown to the client in case the request was forwarded */
+		config.put("apiPath", request.getContextPath());
+
+		if (!"".equals(customizationPath)) {
+			config.put("customizationPath", customizationPath);
+		}
+		if (!"".equals(mobilePath)) {
+			config.put("mobilePath", mobilePath);
+		}
+		if (!"".equals(presenterPath)) {
+			config.put("presenterPath", presenterPath);
+		}
+
+		if (!"".equals(documentationUrl)) {
+			config.put("documentationUrl", documentationUrl);
+		}
+		if (!"".equals(overlayUrl)) {
+			config.put("overlayUrl", overlayUrl);
+		}
+		if (!"".equals(organizationUrl)) {
+			config.put("organizationUrl", organizationUrl);
+		}
+		if (!"".equals(imprintUrl)) {
+			config.put("imprintUrl", imprintUrl);
+		}
+		if (!"".equals(privacyPolicyUrl)) {
+			config.put("privacyPolicyUrl", privacyPolicyUrl);
+		}
+
+		config.put("answerOptionLimit", Integer.valueOf(answerOptionLimit));
+		config.put("parseAnswerOptionFormatting", Boolean.valueOf(parseAnswerOptionFormatting));
+
+		config.put("features", features);
+
+		features.put("mathJax", "true".equals(mathJaxEnabled));
+		features.put("markdown", "true".equals(markdownEnabled));
+		features.put("learningProgress", "true".equals(learningProgressEnabled));
+		features.put("flashcard", "true".equals(flashcardEnabled));
+		features.put("gridSquare", "true".equals(gridSquareEnabled));
+
+		return config;
+	}
+}
diff --git a/src/main/java/de/thm/arsnova/controller/LoginController.java b/src/main/java/de/thm/arsnova/controller/LoginController.java
index 9e4fe2404efa1d74a7aaa1b783d12fb92d7dc9d4..6ab3234e05815d722c5a112f00503a0591364b10 100644
--- a/src/main/java/de/thm/arsnova/controller/LoginController.java
+++ b/src/main/java/de/thm/arsnova/controller/LoginController.java
@@ -19,7 +19,11 @@
 package de.thm.arsnova.controller;
 
 import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.text.MessageFormat;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.List;
 
 import javax.servlet.ServletException;
@@ -33,14 +37,19 @@ import org.scribe.up.session.HttpUserSession;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.HttpStatus;
 import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
 import org.springframework.security.cas.authentication.CasAuthenticationToken;
 import org.springframework.security.cas.web.CasAuthenticationEntryPoint;
 import org.springframework.security.core.Authentication;
+import org.springframework.security.core.AuthenticationException;
 import org.springframework.security.core.GrantedAuthority;
 import org.springframework.security.core.authority.SimpleGrantedAuthority;
 import org.springframework.security.core.context.SecurityContextHolder;
 import org.springframework.security.core.token.Sha512DigestUtils;
+import org.springframework.security.ldap.authentication.LdapAuthenticationProvider;
 import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
 import org.springframework.security.web.util.UrlUtils;
 import org.springframework.stereotype.Controller;
@@ -51,6 +60,7 @@ import org.springframework.web.bind.annotation.ResponseBody;
 import org.springframework.web.servlet.View;
 import org.springframework.web.servlet.view.RedirectView;
 
+import de.thm.arsnova.entities.ServiceDescription;
 import de.thm.arsnova.entities.Session;
 import de.thm.arsnova.entities.User;
 import de.thm.arsnova.exceptions.UnauthorizedException;
@@ -63,6 +73,72 @@ public class LoginController extends AbstractController {
 	private static final int MAX_USERNAME_LENGTH = 15;
 	private static final int MAX_GUESTHASH_LENGTH = 10;
 
+	@Value("${customization.path}")
+	private String customizationPath;
+
+	@Value("${security.guest.enabled}")
+	private String guestEnabled;
+
+	@Value("${security.guest.lecturer.enabled}")
+	private String guestLecturerEnabled;
+
+	@Value("${security.custom-login.enabled}")
+	private String customLoginEnabled;
+
+	@Value("${security.custom-login.title:University}")
+	private String customLoginTitle;
+
+	@Value("${security.custom-login.login-dialog-path}")
+	private String customLoginDialog;
+
+	@Value("${security.custom-login.image:}")
+	private String customLoginImage;
+
+	@Value("${security.user-db.enabled}")
+	private String dbAuthEnabled;
+
+	@Value("${security.user-db.title:ARSnova}")
+	private String dbAuthTitle;
+
+	@Value("${security.user-db.login-dialog-path}")
+	private String dbAuthDialog;
+
+	@Value("${security.user-db.image:}")
+	private String dbAuthImage;
+
+	@Value("${security.ldap.enabled}")
+	private String ldapEnabled;
+
+	@Value("${security.ldap.title:LDAP}")
+	private String ldapTitle;
+
+	@Value("${security.ldap.login-dialog-path}")
+	private String ldapDialog;
+
+	@Value("${security.ldap.image:}")
+	private String ldapImage;
+
+	@Value("${security.cas.enabled}")
+	private String casEnabled;
+
+	@Value("${security.cas.title:CAS}")
+	private String casTitle;
+
+	@Value("${security.cas.image:}")
+	private String casImage;
+
+	@Value("${security.facebook.enabled}")
+	private String facebookEnabled;
+
+	@Value("${security.google.enabled}")
+	private String googleEnabled;
+
+	@Value("${security.twitter.enabled}")
+	private String twitterEnabled;
+
+	@Autowired
+	private DaoAuthenticationProvider daoProvider;
+
 	@Autowired
 	private TwitterProvider twitterProvider;
 
@@ -71,6 +147,9 @@ public class LoginController extends AbstractController {
 
 	@Autowired
 	private FacebookProvider facebookProvider;
+	
+	@Autowired
+	private LdapAuthenticationProvider ldapAuthenticationProvider;
 
 	@Autowired
 	private CasAuthenticationEntryPoint casEntryPoint;
@@ -83,37 +162,117 @@ public class LoginController extends AbstractController {
 
 	public static final Logger LOGGER = LoggerFactory.getLogger(LoginController.class);
 
-	@RequestMapping(value = { "/auth/login", "/doLogin" }, method = RequestMethod.GET)
-	public final View doLogin(
+	@RequestMapping(value = { "/auth/login", "/doLogin" }, method = { RequestMethod.POST, RequestMethod.GET })
+	public final void doLogin(
 			@RequestParam("type") final String type,
-			@RequestParam(value = "user", required = false) final String guestName,
-			@RequestParam(value = "referer", required = false) final String forcedReferer,
-			@RequestParam(value = "successurl", required = false) final String successUrl,
-			@RequestParam(value = "failureurl", required = false) final String failureUrl,
+			@RequestParam(value = "user", required = false) String username,
+			@RequestParam(required = false) final String password,
 			@RequestParam(value = "role", required = false) final UserSessionService.Role role,
 			final HttpServletRequest request,
 			final HttpServletResponse response
-			) throws IOException, ServletException {
+	) throws IOException {
+		String addr = request.getRemoteAddr();
+		if (userService.isBannedFromLogin(addr)) {
+			response.sendError(429, "Too Many Requests");
+
+			return;
+		}
+
 		userSessionService.setRole(role);
 
-		String referer = request.getHeader("referer");
-		if (null != forcedReferer && null != referer && !UrlUtils.isAbsoluteUrl(referer)) {
-			/* Use a url from a request parameter as referer as long as the url is not absolute (to prevent
-			 * abuse of the redirection). */
-			referer = forcedReferer;
+		if ("arsnova".equals(type)) {
+			Authentication authRequest = new UsernamePasswordAuthenticationToken(username, password);
+			try {
+				Authentication auth = daoProvider.authenticate(authRequest);
+				if (auth.isAuthenticated()) {
+					SecurityContextHolder.getContext().setAuthentication(auth);
+					request.getSession(true).setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY,
+							SecurityContextHolder.getContext());
+					
+					return;
+				}
+			} catch (AuthenticationException e) {
+				LOGGER.info("Authentication failed: {}", e.getMessage());
+			}
+
+			userService.increaseFailedLoginCount(addr);
+			response.setStatus(HttpStatus.UNAUTHORIZED.value());
+		} else if ("ldap".equals(type)) {
+			if (!"".equals(username) && !"".equals(password)) {
+				org.springframework.security.core.userdetails.User user =
+						new org.springframework.security.core.userdetails.User(
+							username, password, true, true, true, true, this.getAuthorities()
+						);
+
+				Authentication token = new UsernamePasswordAuthenticationToken(user, password, getAuthorities());
+				try {
+					Authentication auth = ldapAuthenticationProvider.authenticate(token);
+					if (auth.isAuthenticated()) {
+						SecurityContextHolder.getContext().setAuthentication(token);
+						request.getSession(true).setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY,
+								SecurityContextHolder.getContext());
+
+						return;
+					}
+					LOGGER.info("LDAPLOGIN: {}", auth.isAuthenticated());
+				}
+				catch (AuthenticationException e) {
+					LOGGER.info("No LDAP login: {}", e);
+				}
+
+				userService.increaseFailedLoginCount(addr);
+				response.setStatus(HttpStatus.UNAUTHORIZED.value());
+			}
+		} else if ("guest".equals(type)) {
+			List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
+			authorities.add(new SimpleGrantedAuthority("ROLE_GUEST"));
+			if (username == null || !username.startsWith("Guest") || username.length() != MAX_USERNAME_LENGTH) {
+				username = "Guest" + Sha512DigestUtils.shaHex(request.getSession().getId()).substring(0, MAX_GUESTHASH_LENGTH);
+			}
+			org.springframework.security.core.userdetails.User user =
+					new org.springframework.security.core.userdetails.User(
+							username, "", true, true, true, true, authorities
+					);
+			Authentication token = new UsernamePasswordAuthenticationToken(user, null, authorities);
+
+			SecurityContextHolder.getContext().setAuthentication(token);
+			request.getSession(true).setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY,
+					SecurityContextHolder.getContext());
+		}
+	}
+
+	@RequestMapping(value = { "/auth/dialog" }, method = RequestMethod.GET)
+	@ResponseBody
+	public final View dialog(
+			@RequestParam("type") final String type,
+			@RequestParam(value = "successurl", defaultValue = "/") String successUrl,
+			@RequestParam(value = "failureurl", defaultValue = "/") String failureUrl,
+			final HttpServletRequest request,
+			final HttpServletResponse response
+	) throws IOException, ServletException {
+		View result = null;
+
+		/* Use URLs from a request parameters for redirection as long as the 
+		 * URL is not absolute (to prevent abuse of the redirection). */
+		if (UrlUtils.isAbsoluteUrl(successUrl)) {
+			successUrl = "/";
 		}
-		if (null == referer) {
-			referer = "/";
+		if (UrlUtils.isAbsoluteUrl(failureUrl)) {
+			failureUrl = "/";
 		}
 
-		request.getSession().setAttribute("ars-login-success-url",
-				null == successUrl ? referer + "#auth/checkLogin" : successUrl
-				);
-		request.getSession().setAttribute("ars-login-failure-url",
-				null == failureUrl ? referer : failureUrl
-				);
+		/* Workaround until a solution is found to do a redirect which is 
+		 * relative to the server root instead of the context path */
+		String port;
+		if ("https".equals(request.getScheme())) {
+			port = 443 != request.getServerPort() ? ":" + request.getLocalPort() : "";
+		} else {
+			port = 80 != request.getServerPort() ? ":" + request.getLocalPort() : "";
+		}
+		String serverUrl = request.getScheme() + "://" + request.getServerName() + port;
 
-		View result = null;
+		request.getSession().setAttribute("ars-login-success-url", serverUrl + successUrl);
+		request.getSession().setAttribute("ars-login-failure-url", serverUrl + failureUrl);
 
 		if ("cas".equals(type)) {
 			casEntryPoint.commence(request, response, null);
@@ -121,63 +280,124 @@ public class LoginController extends AbstractController {
 			final String authUrl = twitterProvider.getAuthorizationUrl(new HttpUserSession(request));
 			result = new RedirectView(authUrl);
 		} else if ("facebook".equals(type)) {
+			facebookProvider.setFields("id,link");
+			facebookProvider.setScope("");
 			final String authUrl = facebookProvider.getAuthorizationUrl(new HttpUserSession(request));
 			result = new RedirectView(authUrl);
 		} else if ("google".equals(type)) {
 			final String authUrl = googleProvider.getAuthorizationUrl(new HttpUserSession(request));
 			result = new RedirectView(authUrl);
-		} else if ("guest".equals(type)) {
-			result = handleGuestLogin(guestName, successUrl, request, referer);
 		}
 
 		return result;
 	}
 
-	private View handleGuestLogin(final String guestName,
-			final String successUrl, final HttpServletRequest request,
-			final String referer) {
-		View result;
-		final List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
-		authorities.add(new SimpleGrantedAuthority("ROLE_GUEST"));
-		String username = "";
-		if (guestName != null && guestName.startsWith("Guest") && guestName.length() == MAX_USERNAME_LENGTH) {
-			username = guestName;
-		} else {
-			username = "Guest" + Sha512DigestUtils.shaHex(
-					request.getSession().getId()
-					).substring(0, MAX_GUESTHASH_LENGTH);
-		}
-		final org.springframework.security.core.userdetails.User user =
-				new org.springframework.security.core.userdetails.User(
-						username, "", true, true, true, true, authorities
-						);
-		final Authentication token = new UsernamePasswordAuthenticationToken(user, null, authorities);
-
-		SecurityContextHolder.getContext().setAuthentication(token);
-		request.getSession(true).setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY,
-				SecurityContextHolder.getContext());
-		result = new RedirectView(null == successUrl ? referer + "#auth/checkLogin" : successUrl);
-		return result;
-	}
-
 	@RequestMapping(value = { "/auth/", "/whoami" }, method = RequestMethod.GET)
 	@ResponseBody
 	public final User whoami() {
-		userSessionService.setUser(userService.getCurrentUser());
+		userSessionService.setUser(userService.getCurrentUser());		
 		return userService.getCurrentUser();
 	}
 
-	@RequestMapping(value = { "/auth/logout", "/logout" }, method = RequestMethod.GET)
+	@RequestMapping(value = { "/auth/logout", "/logout" }, method = { RequestMethod.POST, RequestMethod.GET } )
 	public final View doLogout(final HttpServletRequest request) {
 		final Authentication auth = SecurityContextHolder.getContext().getAuthentication();
 		userService.removeUserFromMaps(userService.getCurrentUser());
 		request.getSession().invalidate();
 		SecurityContextHolder.clearContext();
 		if (auth instanceof CasAuthenticationToken) {
-			return new RedirectView("/j_spring_cas_security_logout");
+			return new RedirectView("/j_spring_cas_security_logout", true);
 		}
 		return new RedirectView(request.getHeader("referer") != null ? request.getHeader("referer") : "/");
 	}
+	
+	@RequestMapping(value = { "/auth/services" }, method = RequestMethod.GET)
+	@ResponseBody
+	public final List<ServiceDescription> getServices(final HttpServletRequest request) {
+		List<ServiceDescription> services = new ArrayList<ServiceDescription>();
+
+		/* The first parameter is replaced by the backend, the second one by the frondend */
+		String dialogUrl = request.getContextPath() + "/auth/dialog?type={0}&successurl='{0}'";
+
+		if ("true".equals(guestEnabled)) {
+			ServiceDescription sdesc = new ServiceDescription(
+				"guest",
+				"Guest",
+				null
+			);
+			if (!"true".equals(guestLecturerEnabled)) {
+				sdesc.setAllowLecturer(false);
+			}
+			services.add(sdesc);
+		}
+
+		if ("true".equals(customLoginEnabled) && !"".equals(customLoginDialog)) {
+			services.add(new ServiceDescription(
+				"custom",
+				customLoginTitle,
+				customizationPath + "/" + customLoginDialog + "?redirect={0}",
+				customLoginImage
+			));
+		}
+
+		if ("true".equals(dbAuthEnabled) && !"".equals(dbAuthDialog)) {
+			services.add(new ServiceDescription(
+				"arsnova",
+				dbAuthTitle,
+				customizationPath + "/" + dbAuthDialog + "?redirect={0}",
+				dbAuthImage
+			));
+		}
+
+		if ("true".equals(ldapEnabled) && !"".equals(ldapDialog)) {
+			services.add(new ServiceDescription(
+				"ldap",
+				ldapTitle,
+				customizationPath + "/" + ldapDialog + "?redirect={0}",
+				ldapImage
+			));
+		}
+
+		if ("true".equals(casEnabled)) {
+			services.add(new ServiceDescription(
+				"cas",
+				casTitle,
+				MessageFormat.format(dialogUrl, "cas")
+			));
+		}
+
+		if ("true".equals(facebookEnabled)) {
+			services.add(new ServiceDescription(
+				"facebook",
+				"Facebook",
+				MessageFormat.format(dialogUrl, "facebook")
+			));
+		}
+
+		if ("true".equals(googleEnabled)) {
+			services.add(new ServiceDescription(
+				"google",
+				"Google",
+				MessageFormat.format(dialogUrl, "google")
+			));
+		}
+
+		if ("true".equals(twitterEnabled)) {
+			services.add(new ServiceDescription(
+				"twitter",
+				"Twitter",
+				MessageFormat.format(dialogUrl, "twitter")
+			));
+		}
+
+		return services;
+	}
+
+	private Collection<GrantedAuthority> getAuthorities() {
+		List<GrantedAuthority> authList = new ArrayList<GrantedAuthority>();
+		authList.add(new SimpleGrantedAuthority("ROLE_USER"));
+		return authList;
+	}
 
 	@RequestMapping(value = { "/test/me" }, method = RequestMethod.GET)
 	@ResponseBody
diff --git a/src/main/java/de/thm/arsnova/controller/UserController.java b/src/main/java/de/thm/arsnova/controller/UserController.java
new file mode 100644
index 0000000000000000000000000000000000000000..9d3fe607d20642fb5d2d99f19915f89357d9ef5f
--- /dev/null
+++ b/src/main/java/de/thm/arsnova/controller/UserController.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2012 THM webMedia
+ *
+ * This file is part of ARSnova.
+ *
+ * ARSnova is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * ARSnova 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package de.thm.arsnova.controller;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+
+import de.thm.arsnova.entities.DbUser;
+import de.thm.arsnova.services.IUserService;
+import de.thm.arsnova.services.UserSessionService;
+
+@Controller
+@RequestMapping("/user")
+public class UserController extends AbstractController {
+	@Value("${security.guest.enabled}")
+	private String guestEnabled;
+	@Value("${security.guest.lecturer.enabled}")
+	private String guestLecturerEnabled;
+	@Value("${security.cas.enabled}")
+	private String casEnabled;
+	@Value("${security.ldap.enabled}")
+	private String ldapEnabled;
+	@Value("${security.facebook.enabled}")
+	private String facebookEnabled;
+	@Value("${security.google.enabled}")
+	private String googleEnabled;
+	@Value("${security.twitter.enabled}")
+	private String twitterEnabled;
+
+	@Autowired
+	private DaoAuthenticationProvider daoProvider;
+
+	@Autowired
+	private IUserService userService;
+
+	@Autowired
+	private UserSessionService userSessionService;
+
+	public static final Logger LOGGER = LoggerFactory
+			.getLogger(UserController.class);
+
+	@RequestMapping(value = { "/register" }, method = RequestMethod.POST)
+	public final void register(@RequestParam final String username,
+			@RequestParam final String password,
+			final HttpServletRequest request, final HttpServletResponse response) {
+		if (null != userService.createDbUser(username, password)) {
+			return;
+		}
+
+		/* TODO: Improve error handling: send reason to client */
+		response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
+	}
+
+	@RequestMapping(value = { "/{username}/activate" }, method = { RequestMethod.POST,
+			RequestMethod.GET })
+	public final void activate(
+			@PathVariable final String username,
+			@RequestParam final String key, final HttpServletRequest request,
+			final HttpServletResponse response) {
+		DbUser dbUser = userService.getDbUser(username);
+		if (null != dbUser && key.equals(dbUser.getActivationKey())) {
+			dbUser.setActivationKey(null);
+			userService.updateDbUser(dbUser);
+
+			return;
+		}
+
+		response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
+	}
+
+	@RequestMapping(value = { "/{username}" }, method = RequestMethod.DELETE)
+	public final void activate(
+			@PathVariable final String username,
+			final HttpServletRequest request,
+			final HttpServletResponse response) {
+		if (null == userService.deleteDbUser(username)) {
+			response.setStatus(HttpServletResponse.SC_NOT_FOUND);
+		}
+	}
+
+	@RequestMapping(value = { "/{username}/resetpassword" }, method = RequestMethod.POST)
+	public final void resetPassword(
+			@PathVariable final String username,
+			@RequestParam(required = false) final String key,
+			@RequestParam(required = false) final String password,
+			final HttpServletRequest request,
+			final HttpServletResponse response) {
+		DbUser dbUser = userService.getDbUser(username);
+		if (null == dbUser) {
+			response.setStatus(HttpServletResponse.SC_NOT_FOUND);
+
+			return;
+		}
+
+		if (null != key) {
+			if (!userService.resetPassword(dbUser, key, password)) {
+				response.setStatus(HttpServletResponse.SC_FORBIDDEN);
+			}
+		} else {
+			userService.initiatePasswordReset(username);
+		}
+	}
+}
diff --git a/src/main/java/de/thm/arsnova/controller/WelcomeController.java b/src/main/java/de/thm/arsnova/controller/WelcomeController.java
index 588dad75e08a442d13c23a4152e9a6140ec23c83..f54afa314f2ec301eab3fb672eddf3c3ddd30c1d 100644
--- a/src/main/java/de/thm/arsnova/controller/WelcomeController.java
+++ b/src/main/java/de/thm/arsnova/controller/WelcomeController.java
@@ -18,22 +18,33 @@
  */
 package de.thm.arsnova.controller;
 
+import java.util.HashMap;
+
 import javax.servlet.http.HttpServletRequest;
 
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Controller;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RequestMethod;
-import org.springframework.web.servlet.ModelAndView;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.servlet.View;
+import org.springframework.web.servlet.view.RedirectView;
 
 @Controller
 public class WelcomeController extends AbstractController {
+
+	@Value("${mobile.path}")
+	String mobileContextPath;
+
 	@RequestMapping(value = "/", method = RequestMethod.GET)
-	public final ModelAndView home(final HttpServletRequest request) {
-		String referer = request.getHeader("referer");
-		String target = "index.html";
-		if (referer != null && referer.endsWith("dojo-index.html")) {
-			target = "dojo-index.html";
-		}
-		return new ModelAndView("redirect:/" + target);
+	public final View home(final HttpServletRequest request) {
+		return new RedirectView(mobileContextPath + "/", false);
 	}
+
+	@RequestMapping(value = "/", method = RequestMethod.GET, produces = "application/json")
+	@ResponseBody
+	public final HashMap<String, Object> jsonHome(final HttpServletRequest request) {
+		return new HashMap<String, Object>();
+	}
+
 }
diff --git a/src/main/java/de/thm/arsnova/dao/CouchDBDao.java b/src/main/java/de/thm/arsnova/dao/CouchDBDao.java
index edc69e4bfab1585d9dc312d50e19488a9483f81d..56a26feae04be2283ed48103085b43f599c58c94 100644
--- a/src/main/java/de/thm/arsnova/dao/CouchDBDao.java
+++ b/src/main/java/de/thm/arsnova/dao/CouchDBDao.java
@@ -41,6 +41,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
 import org.springframework.transaction.annotation.Isolation;
 import org.springframework.transaction.annotation.Transactional;
 
@@ -51,6 +52,7 @@ import com.fourspaces.couchdb.ViewResults;
 
 import de.thm.arsnova.connector.model.Course;
 import de.thm.arsnova.entities.Answer;
+import de.thm.arsnova.entities.DbUser;
 import de.thm.arsnova.entities.FoodVote;
 import de.thm.arsnova.entities.InterposedQuestion;
 import de.thm.arsnova.entities.InterposedReadingCount;
@@ -63,6 +65,7 @@ import de.thm.arsnova.entities.VisitedSession;
 import de.thm.arsnova.exceptions.NotFoundException;
 import de.thm.arsnova.services.ISessionService;
 
+@Component("databaseDao")
 public class CouchDBDao implements IDatabaseDao {
 
 	@Autowired
@@ -392,6 +395,11 @@ public class CouchDBDao implements IDatabaseDao {
 					final Collection<VisitedSession> visitedSessions = JSONArray.toCollection(vs, VisitedSession.class);
 					loggedIn.setVisitedSessions(new ArrayList<VisitedSession>(visitedSessions));
 				}
+
+				/* Do not clutter CouchDB. Only update once every 3 hours per session. */
+				if (loggedIn.getSessionId().equals(session.get_id()) && loggedIn.getTimestamp() > System.currentTimeMillis() - 3 * 3600000) {
+					return loggedIn;
+				}
 			}
 
 			loggedIn.setUser(user.getUsername());
@@ -427,6 +435,11 @@ public class CouchDBDao implements IDatabaseDao {
 	@Override
 	public final void updateSessionOwnerActivity(final Session session) {
 		try {
+			/* Do not clutter CouchDB. Only update once every 3 hours. */
+			if (session.getLastOwnerActivity() > System.currentTimeMillis() - 3 * 3600000) {
+				return;
+			}
+
 			session.setLastOwnerActivity(System.currentTimeMillis());
 			final JSONObject json = JSONObject.fromObject(session);
 			getDatabase().saveDocument(new Document(json));
@@ -1407,4 +1420,64 @@ public class CouchDBDao implements IDatabaseDao {
 
 		return new AbstractMap.SimpleEntry<Integer, Integer>((int)Math.round(myProgress*100), courseProgress);
 	}
+
+	public DbUser createOrUpdateUser(DbUser user) {
+		try {
+			String id = user.getId();
+			String rev = user.getRev();
+			Document d = new Document();
+
+			if (null != id) {
+				d = database.getDocument(id, rev);
+			}
+
+			d.put("type", "userdetails");
+			d.put("username", user.getUsername());
+			d.put("password", user.getPassword());
+			d.put("activationKey", user.getActivationKey());
+			d.put("passwordResetKey", user.getPasswordResetKey());
+			d.put("passwordResetTime", user.getPasswordResetTime());
+			d.put("creation", user.getCreation());
+			d.put("lastLogin", user.getLastLogin());
+
+			database.saveDocument(d, id);
+			user.setId(d.getId());
+			user.setRev(d.getRev());
+
+			return user;
+		} catch (IOException e) {
+			LOGGER.error("Could not save user {}", user);
+		}
+
+		return null;
+	}
+
+	@Override
+	public DbUser getUser(String username) {
+		NovaView view = new NovaView("user/all");
+		view.setKey(username);
+		ViewResults results = this.getDatabase().view(view);
+
+		if (results.getJSONArray("rows").optJSONObject(0) == null) {
+			return null;
+		}
+
+		return (DbUser) JSONObject.toBean(
+			results.getJSONArray("rows").optJSONObject(0).optJSONObject("value"),
+			DbUser.class
+		);
+	}
+
+	@Override
+	public boolean deleteUser(DbUser dbUser) {
+		try {
+			this.deleteDocument(dbUser.getId());
+
+			return true;
+		} catch (IOException e) {
+			LOGGER.error("Could not delete user {}", dbUser.getId());
+		}
+
+		return false;
+	}
 }
diff --git a/src/main/java/de/thm/arsnova/dao/IDatabaseDao.java b/src/main/java/de/thm/arsnova/dao/IDatabaseDao.java
index 40bb3f97ca33db3970cc8dd5f04f072f5d879471..67eaeaede0dd6e5c367b0d2465a56cd72a2cf73b 100644
--- a/src/main/java/de/thm/arsnova/dao/IDatabaseDao.java
+++ b/src/main/java/de/thm/arsnova/dao/IDatabaseDao.java
@@ -24,6 +24,7 @@ import java.util.List;
 
 import de.thm.arsnova.connector.model.Course;
 import de.thm.arsnova.entities.Answer;
+import de.thm.arsnova.entities.DbUser;
 import de.thm.arsnova.entities.FoodVote;
 import de.thm.arsnova.entities.InterposedQuestion;
 import de.thm.arsnova.entities.InterposedReadingCount;
@@ -163,6 +164,12 @@ public interface IDatabaseDao {
 
 	void deleteAllQuestionsAnswers(Session session);
 
+	DbUser createOrUpdateUser(DbUser user);
+
+	DbUser getUser(String username);
+
+	boolean deleteUser(DbUser dbUser);
+
 	int getLearningProgress(Session session);
 
 	SimpleEntry<Integer, Integer> getMyLearningProgress(Session session, User user);
diff --git a/src/main/java/de/thm/arsnova/entities/DbUser.java b/src/main/java/de/thm/arsnova/entities/DbUser.java
new file mode 100644
index 0000000000000000000000000000000000000000..7d185c25e5b4b435bec2bfc20f3f78715899f3e9
--- /dev/null
+++ b/src/main/java/de/thm/arsnova/entities/DbUser.java
@@ -0,0 +1,100 @@
+package de.thm.arsnova.entities;
+
+public class DbUser {
+	private String id;
+	private String rev;
+	private String username;
+	private String password;
+	private String activationKey;
+	private String passwordResetKey;
+	private long passwordResetTime;
+	private long creation;
+	private long lastLogin;
+
+	public String getId() {
+		return id;
+	}
+
+	public void setId(String id) {
+		this.id = id;
+	}
+
+	/* CouchDB deserialization */
+	public void set_id(String id) {
+		this.id = id;
+	}
+
+	public String getRev() {
+		return rev;
+	}
+
+	public void setRev(String rev) {
+		this.rev = rev;
+	}
+
+	/* CouchDB deserialization */
+	public void set_rev(String rev) {
+		this.rev = rev;
+	}
+
+	public String getUsername() {
+		return username;
+	}
+
+	public void setUsername(String username) {
+		this.username = username;
+	}
+
+	public String getPassword() {
+		return password;
+	}
+
+	public void setPassword(String password) {
+		this.password = password;
+	}
+
+	public String getActivationKey() {
+		return activationKey;
+	}
+
+	public void setActivationKey(String activationKey) {
+		this.activationKey = activationKey;
+	}
+
+	public String getPasswordResetKey() {
+		return passwordResetKey;
+	}
+
+	public void setPasswordResetKey(String passwordResetKey) {
+		this.passwordResetKey = passwordResetKey;
+	}
+
+	public long getPasswordResetTime() {
+		return passwordResetTime;
+	}
+
+	public void setPasswordResetTime(long passwordResetTime) {
+		this.passwordResetTime = passwordResetTime;
+	}
+
+	public long getCreation() {
+		return creation;
+	}
+
+	public void setCreation(long creation) {
+		this.creation = creation;
+	}
+
+	public long getLastLogin() {
+		return lastLogin;
+	}
+
+	public void setLastLogin(long lastLogin) {
+		this.lastLogin = lastLogin;
+	}
+
+	/* CouchDB deserialization */
+	public void setType(String type) {
+		/* no op */
+	}
+}
diff --git a/src/main/java/de/thm/arsnova/entities/ServiceDescription.java b/src/main/java/de/thm/arsnova/entities/ServiceDescription.java
new file mode 100644
index 0000000000000000000000000000000000000000..f9199ac039a0917062dd4c2c1e6b9528f2013ad1
--- /dev/null
+++ b/src/main/java/de/thm/arsnova/entities/ServiceDescription.java
@@ -0,0 +1,80 @@
+package de.thm.arsnova.entities;
+
+public class ServiceDescription {
+	private String id;
+	private String name;
+	private String dialogUrl;
+	private String image;
+	private int order = 0;
+	private boolean allowLecturer = true;
+
+	public ServiceDescription(String id, String name, String dialogUrl) {
+		this.id = id;
+		this.name = name;
+		this.dialogUrl = dialogUrl;
+	}
+
+	public ServiceDescription(String id, String name, String dialogUrl, String image) {
+		this.id = id;
+		this.name = name;
+		this.dialogUrl = dialogUrl;
+		if (!"".equals(image)) {
+			this.image = image;
+		}
+	}
+
+	public ServiceDescription(String id, String name, String dialogUrl, boolean allowLecturer) {
+		this.id = id;
+		this.name = name;
+		this.dialogUrl = dialogUrl;
+		this.allowLecturer = allowLecturer;
+	}
+
+	public String getId() {
+		return id;
+	}
+
+	public void setId(String id) {
+		this.id = id;
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	public String getDialogUrl() {
+		return dialogUrl;
+	}
+
+	public void setDialogUrl(String dialogUrl) {
+		this.dialogUrl = dialogUrl;
+	}
+
+	public String getImage() {
+		return image;
+	}
+
+	public void setImage(String image) {
+		this.image = image;
+	}
+
+	public int getOrder() {
+		return order;
+	}
+
+	public void setOrder(int order) {
+		this.order = order;
+	}
+
+	public boolean isAllowLecturer() {
+		return allowLecturer;
+	}
+
+	public void setAllowLecturer(boolean allowLecturer) {
+		this.allowLecturer = allowLecturer;
+	}
+}
diff --git a/src/main/java/de/thm/arsnova/entities/User.java b/src/main/java/de/thm/arsnova/entities/User.java
index 2f9c290bf2af3a41d6eac5b16d329d413e0ee4ff..ba64e2ffb0f94cbac3dc93e45c40f1b8545669ef 100644
--- a/src/main/java/de/thm/arsnova/entities/User.java
+++ b/src/main/java/de/thm/arsnova/entities/User.java
@@ -17,7 +17,9 @@ public class User implements Serializable {
 	public static final String FACEBOOK = "facebook";
 	public static final String THM = "thm";
 	public static final String LDAP = "ldap";
+	public static final String ARSNOVA = "arsnova";
 	public static final String ANONYMOUS = "anonymous";
+	public static final String GUEST = "guest";
 
 	private static final long serialVersionUID = 1L;
 	private String username;
diff --git a/src/main/java/de/thm/arsnova/security/DbUserDetailsService.java b/src/main/java/de/thm/arsnova/security/DbUserDetailsService.java
new file mode 100644
index 0000000000000000000000000000000000000000..cbee6375e0b28776e1bead12adfedbeaf0b7e1e7
--- /dev/null
+++ b/src/main/java/de/thm/arsnova/security/DbUserDetailsService.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2014 THM webMedia
+ *
+ * This file is part of ARSnova.
+ *
+ * ARSnova is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * ARSnova 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package de.thm.arsnova.security;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.security.core.userdetails.User;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
+import org.springframework.stereotype.Service;
+
+import de.thm.arsnova.dao.IDatabaseDao;
+import de.thm.arsnova.entities.DbUser;
+
+@Service
+public class DbUserDetailsService implements UserDetailsService {
+	@Autowired
+	private IDatabaseDao dao;
+
+	public static final Logger LOGGER = LoggerFactory
+			.getLogger(DbUserDetailsService.class);
+
+	@Override
+	public UserDetails loadUserByUsername(String username) {
+		LOGGER.debug("Load user: " + username);
+		DbUser dbUser = dao.getUser(username);
+		if (null == dbUser) {
+			throw new UsernameNotFoundException("User does not exist.");
+		}
+
+		final List<GrantedAuthority> grantedAuthorities = new ArrayList<GrantedAuthority>();
+		grantedAuthorities.add(new SimpleGrantedAuthority("ROLE_USER"));
+		grantedAuthorities.add(new SimpleGrantedAuthority("ROLE_DB_USER"));
+
+		return new User(username, dbUser.getPassword(),
+				null == dbUser.getActivationKey(), true, true, true,
+				grantedAuthorities);
+	}
+}
diff --git a/src/main/java/de/thm/arsnova/services/IUserService.java b/src/main/java/de/thm/arsnova/services/IUserService.java
index 8716c3ebcaba693e88441cd0894bd32c46ce73d0..ee4258519f9b674dd2fd7159384ccd9e3d5710cf 100644
--- a/src/main/java/de/thm/arsnova/services/IUserService.java
+++ b/src/main/java/de/thm/arsnova/services/IUserService.java
@@ -23,10 +23,15 @@ import java.util.Map;
 import java.util.Set;
 import java.util.UUID;
 
+import de.thm.arsnova.entities.DbUser;
 import de.thm.arsnova.entities.User;
 
 public interface IUserService {
 	User getCurrentUser();
+	
+	boolean isBannedFromLogin(String addr);
+
+	void increaseFailedLoginCount(String addr);
 
 	User getUser2SocketId(UUID socketId);
 
@@ -51,4 +56,16 @@ public interface IUserService {
 	void removeUserFromMaps(User user);
 
 	int loggedInUsers();
+
+	DbUser getDbUser(String username);
+
+	DbUser createDbUser(String username, String password);
+
+	DbUser updateDbUser(DbUser dbUser);
+
+	DbUser deleteDbUser(String username);
+
+	void initiatePasswordReset(String username);
+
+	boolean resetPassword(DbUser dbUser, String key, String password);
 }
diff --git a/src/main/java/de/thm/arsnova/services/SessionService.java b/src/main/java/de/thm/arsnova/services/SessionService.java
index 595c0567a9e7b27320fafc439ab3ffe7b891c309..d2742c182e907e67e451b35661d7baa360b378c7 100644
--- a/src/main/java/de/thm/arsnova/services/SessionService.java
+++ b/src/main/java/de/thm/arsnova/services/SessionService.java
@@ -84,9 +84,16 @@ public class SessionService implements ISessionService {
 	public final Session joinSession(final String keyword, final UUID socketId) {
 		/* Socket.IO solution */
 
-		final Session session = databaseDao.getSession(keyword);
+		Session session = null;
+		try {
+			session = databaseDao.getSession(keyword);
+		} catch (NotFoundException e) {
+
+		}
 		if (null == session) {
-			throw new NotFoundException();
+			userService.removeUserFromSessionBySocketId(socketId);
+
+			return null;
 		}
 		final User user = userService.getUser2SocketId(socketId);
 
diff --git a/src/main/java/de/thm/arsnova/services/StatisticsService.java b/src/main/java/de/thm/arsnova/services/StatisticsService.java
index 435e249cfc47ca91ec1522bd615095d3236d7b5a..5446fd0d8cc1cfad28c6258ecf355402fb9ca715 100644
--- a/src/main/java/de/thm/arsnova/services/StatisticsService.java
+++ b/src/main/java/de/thm/arsnova/services/StatisticsService.java
@@ -15,6 +15,9 @@ public class StatisticsService implements IStatisticsService {
 	@Autowired
 	private IDatabaseDao databaseDao;
 
+	@Autowired
+	private IUserService userService;
+
 	@Autowired
 	private SessionRegistry sessionRegistry;
 
@@ -27,8 +30,10 @@ public class StatisticsService implements IStatisticsService {
 		statistics.setClosedSessions(databaseDao.countClosedSessions());
 		statistics.setAnswers(databaseDao.countAnswers());
 		statistics.setQuestions(databaseDao.countQuestions());
-		statistics.setActiveUsers(databaseDao.countActiveUsers(since));
+		/* TODO: Are both of the following do the same, now? If so, remove one of them. */
+		statistics.setActiveUsers(userService.loggedInUsers());
 		statistics.setLoggedinUsers(countLoggedInUsers());
+
 		return statistics;
 	}
 
diff --git a/src/main/java/de/thm/arsnova/services/UserService.java b/src/main/java/de/thm/arsnova/services/UserService.java
index 96785274ba47d69828fe815f27436bf06563def5..adc41130b4ab431d0878f2f0828ce7214afb4f07 100644
--- a/src/main/java/de/thm/arsnova/services/UserService.java
+++ b/src/main/java/de/thm/arsnova/services/UserService.java
@@ -1,5 +1,10 @@
 package de.thm.arsnova.services;
 
+import java.io.UnsupportedEncodingException;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
@@ -7,37 +12,64 @@ import java.util.Map.Entry;
 import java.util.Set;
 import java.util.UUID;
 import java.util.concurrent.ConcurrentHashMap;
+import java.util.regex.Pattern;
 
 import javax.annotation.PreDestroy;
+import javax.mail.MessagingException;
+import javax.mail.internet.MimeMessage;
 
+import org.apache.commons.lang.RandomStringUtils;
+import org.apache.commons.lang.StringUtils;
 import org.scribe.up.profile.facebook.FacebookProfile;
 import org.scribe.up.profile.google.Google2Profile;
 import org.scribe.up.profile.twitter.TwitterProfile;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.mail.MailException;
+import org.springframework.mail.javamail.JavaMailSender;
+import org.springframework.mail.javamail.MimeMessageHelper;
 import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.security.authentication.AnonymousAuthenticationToken;
 import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
 import org.springframework.security.cas.authentication.CasAuthenticationToken;
 import org.springframework.security.core.Authentication;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
 import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+import org.springframework.security.crypto.keygen.BytesKeyGenerator;
+import org.springframework.security.crypto.keygen.KeyGenerators;
+import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Isolation;
 import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.util.UriUtils;
 
 import com.github.leleuj.ss.oauth.client.authentication.OAuthAuthenticationToken;
 
 import de.thm.arsnova.dao.IDatabaseDao;
+import de.thm.arsnova.entities.DbUser;
 import de.thm.arsnova.entities.User;
+import de.thm.arsnova.exceptions.BadRequestException;
+import de.thm.arsnova.exceptions.NotFoundException;
 import de.thm.arsnova.exceptions.UnauthorizedException;
 import de.thm.arsnova.socket.ARSnovaSocketIOServer;
 
+@Service
 public class UserService implements IUserService {
 
 	private static final int DEFAULT_SCHEDULER_DELAY_MS = 60000;
 
+	private static final int LOGIN_TRY_RESET_DELAY_MS = 30 * 1000;
+
+	private static final int LOGIN_BAN_RESET_DELAY_MS = 2 * 60 * 1000;
+
 	private static final int MAX_USER_INACTIVE_SECONDS = 120;
 
+	private static final int REPEATED_PASSWORD_RESET_DELAY_MS = 3 * 60 * 1000;
+	
+	private static final int PASSWORD_RESET_KEY_DURABILITY_MS = 2 * 60 * 60 * 1000;
+
 	public static final Logger LOGGER = LoggerFactory.getLogger(UserService.class);
 
 	private static final ConcurrentHashMap<UUID, User> socketid2user = new ConcurrentHashMap<UUID, User>();
@@ -54,6 +86,72 @@ public class UserService implements IUserService {
 	@Autowired
 	private ARSnovaSocketIOServer socketIoServer;
 
+	@Autowired
+	private JavaMailSender mailSender;
+
+	@Value("${root-url}")
+	private String rootUrl;
+
+	@Value("${customization.path}")
+	private String customizationPath;
+
+	@Value("${security.user-db.allowed-email-domains}")
+	private String allowedEmailDomains;
+
+	@Value("${security.user-db.activation-path}")
+	private String activationPath;
+
+	@Value("${security.user-db.reset-password-path}")
+	private String resetPasswordPath;
+
+	@Value("${mail.sender.address}")
+	private String mailSenderAddress;
+
+	@Value("${mail.sender.name}")
+	private String mailSenderName;
+
+	@Value("${security.user-db.registration-mail.subject}")
+	private String regMailSubject;
+
+	@Value("${security.user-db.registration-mail.body}")
+	private String regMailBody;
+
+	@Value("${security.user-db.reset-password-mail.subject}")
+	private String resetPasswordMailSubject;
+
+	@Value("${security.user-db.reset-password-mail.body}")
+	private String resetPasswordMailBody;
+
+	@Value("${security.authentication.login-try-limit}")
+	private int loginTryLimit;
+
+	private Pattern mailPattern;
+	private BytesKeyGenerator keygen;
+	private BCryptPasswordEncoder encoder;
+	private ConcurrentHashMap<String, Byte> loginTries;
+	private Set<String> loginBans;
+
+	{
+		loginTries = new ConcurrentHashMap<String, Byte>();
+		loginBans = Collections.synchronizedSet(new HashSet<String>());
+	}
+
+	@Scheduled(fixedDelay = LOGIN_TRY_RESET_DELAY_MS)
+	public void resetLoginTries() {
+		if (loginTries.size() > 0) {
+			LOGGER.debug("Reset failed login counters.");
+			loginTries.clear();
+		}
+	}
+
+	@Scheduled(fixedDelay = LOGIN_BAN_RESET_DELAY_MS)
+	public void resetLoginBans() {
+		if (loginBans.size() > 0) {
+			LOGGER.info("Reset temporary login bans.");
+			loginBans.clear();
+		}
+	}
+
 	@Scheduled(fixedDelay = DEFAULT_SCHEDULER_DELAY_MS)
 	public final void removeInactiveUsersFromLegacyMap() {
 		final List<String> usernames = databaseDao.getActiveUsers(MAX_USER_INACTIVE_SECONDS);
@@ -96,6 +194,11 @@ public class UserService implements IUserService {
 		} else if (authentication instanceof UsernamePasswordAuthenticationToken) {
 			final UsernamePasswordAuthenticationToken token = (UsernamePasswordAuthenticationToken) authentication;
 			user = new User(token);
+			if (authentication.getAuthorities().contains(new SimpleGrantedAuthority("ROLE_GUEST"))) {
+				user.setType(User.GUEST);
+			} else if (authentication.getAuthorities().contains(new SimpleGrantedAuthority("ROLE_DB_USER"))) {
+				user.setType(User.ARSNOVA);
+			}
 		}
 
 		if (user == null || user.getUsername().equals("anonymous")) {
@@ -121,6 +224,24 @@ public class UserService implements IUserService {
 		return user;
 	}
 
+	public boolean isBannedFromLogin(String addr) {
+		return loginBans.contains(addr);
+	}
+
+	public void increaseFailedLoginCount(String addr) {
+		Byte tries = (Byte) loginTries.get(addr);
+		if (null == tries) {
+			tries = 0;
+		}
+		if (tries < 5) {
+			loginTries.put(addr, ++tries);
+			if (5 == tries) {
+				LOGGER.info("Temporarily banned {} from login.", new Object[] {addr});
+				loginBans.add(addr);
+			}
+		}
+	}
+
 	@Override
 	public User getUser2SocketId(final UUID socketId) {
 		return socketid2user.get(socketId);
@@ -236,4 +357,191 @@ public class UserService implements IUserService {
 	public int loggedInUsers() {
 		return user2sessionLegacy.size();
 	}
+
+	@Override
+	public DbUser getDbUser(String username) {
+		return databaseDao.getUser(username);
+	}
+
+	@Override
+	public DbUser createDbUser(String username, String password) {
+		if (null == keygen) {
+			keygen = KeyGenerators.secureRandom(32);
+		}
+
+		if (null == mailPattern) {
+			parseMailAddressPattern();
+		}
+
+		if (null == mailPattern || !mailPattern.matcher(username).matches()) {
+			return null;
+		}
+
+		if (null != databaseDao.getUser(username)) {
+			return null;
+		}
+
+		DbUser dbUser = new DbUser();
+		dbUser.setUsername(username);
+		dbUser.setPassword(encodePassword(password));
+		dbUser.setActivationKey(RandomStringUtils.randomAlphanumeric(32));
+		dbUser.setCreation(System.currentTimeMillis());
+
+		DbUser result = databaseDao.createOrUpdateUser(dbUser);
+		if (null != result) {
+			sendActivationEmail(result);
+		}
+
+		return result;
+	}
+
+	public String encodePassword(String password) {
+		if (null == encoder) {
+			encoder = new BCryptPasswordEncoder(12);
+		}
+
+		return encoder.encode(password);
+	}
+
+	public void sendActivationEmail(DbUser dbUser) {
+		String activationUrl;
+		try {
+			activationUrl = MessageFormat.format(
+				"{0}{1}/{2}?action=activate&username={3}&key={4}",
+				rootUrl,
+				customizationPath,
+				activationPath,
+				UriUtils.encodeQueryParam(dbUser.getUsername(), "UTF-8"),
+				dbUser.getActivationKey()
+			);
+		} catch (UnsupportedEncodingException e1) {
+			LOGGER.error(e1.getMessage());
+
+			return;
+		}
+
+		sendEmail(dbUser, regMailSubject, MessageFormat.format(regMailBody, activationUrl));
+	}
+
+	private void parseMailAddressPattern() {
+		/* TODO: Add Unicode support */
+
+		List<String> domainList = Arrays.asList(allowedEmailDomains.split(","));
+
+		if (domainList.size() > 0) {
+			List<String> patterns = new ArrayList<String>();
+			if (domainList.contains("*")) {
+				patterns.add("([a-z0-9-]+\\.)+[a-z0-9-]+");
+			} else {
+				Pattern patternPattern = Pattern.compile("[a-z0-9.*-]+", Pattern.CASE_INSENSITIVE);
+				for (String patternStr : domainList) {
+					if (patternPattern.matcher(patternStr).matches()) {
+						patterns.add(patternStr.replaceAll("[.]", "[.]").replaceAll("[*]", "[a-z0-9-]+?"));
+					}
+				}
+			}
+
+			mailPattern = Pattern.compile("[a-z0-9._-]+?@(" + StringUtils.join(patterns, "|") + ")", Pattern.CASE_INSENSITIVE);
+			LOGGER.info("Allowed e-mail addresses (pattern) for registration: " + mailPattern.pattern());
+		}
+	}
+
+	@Override
+	public DbUser updateDbUser(DbUser dbUser) {
+		if (null != dbUser.getId()) {
+			return databaseDao.createOrUpdateUser(dbUser);
+		}
+
+		return null;
+	}
+
+	@Override
+	public DbUser deleteDbUser(String username) {
+		User user = getCurrentUser();
+		if (!user.getUsername().equals(username)
+				&& SecurityContextHolder.getContext().getAuthentication().getAuthorities()
+						.contains(new SimpleGrantedAuthority("ROLE_ADMIN"))) {
+			throw new UnauthorizedException();
+		}
+
+		DbUser dbUser = databaseDao.getUser(username);
+		if (null == dbUser) {
+			throw new NotFoundException();
+		}
+
+		databaseDao.deleteUser(dbUser);
+
+		return dbUser;
+	}
+
+	@Override
+	public void initiatePasswordReset(String username) {
+		DbUser dbUser = databaseDao.getUser(username);
+		if (null == dbUser) {
+			throw new NotFoundException();
+		}
+		if (System.currentTimeMillis() < dbUser.getPasswordResetTime() + REPEATED_PASSWORD_RESET_DELAY_MS) {
+			throw new BadRequestException();
+		}
+
+		dbUser.setPasswordResetKey(RandomStringUtils.randomAlphanumeric(32));
+		dbUser.setPasswordResetTime(System.currentTimeMillis());
+		databaseDao.createOrUpdateUser(dbUser);
+
+		String resetPasswordUrl;
+		try {
+			resetPasswordUrl = MessageFormat.format(
+				"{0}{1}/{2}?action=resetpassword&username={3}&key={4}",
+				rootUrl,
+				customizationPath,
+				resetPasswordPath,
+				UriUtils.encodeQueryParam(dbUser.getUsername(), "UTF-8"),
+				dbUser.getPasswordResetKey()
+			);
+		} catch (UnsupportedEncodingException e1) {
+			LOGGER.error(e1.getMessage());
+
+			return;
+		}
+
+		sendEmail(dbUser, resetPasswordMailSubject, MessageFormat.format(resetPasswordMailBody, resetPasswordUrl));
+	}
+
+	@Override
+	public boolean resetPassword(DbUser dbUser, String key, String password) {
+		if (null == key || "".equals(key) || !key.equals(dbUser.getPasswordResetKey())) {
+			return false;
+		}
+		if (System.currentTimeMillis() > dbUser.getPasswordResetTime() + PASSWORD_RESET_KEY_DURABILITY_MS) {
+			dbUser.setPasswordResetKey(null);
+			dbUser.setPasswordResetTime(0);
+			updateDbUser(dbUser);
+
+			return false;
+		}
+
+		dbUser.setPassword(encodePassword(password));
+		dbUser.setPasswordResetKey(null);
+		updateDbUser(dbUser);
+
+		return true;
+	}
+
+	private void sendEmail(DbUser dbUser, String subject, String body) {
+		MimeMessage msg = mailSender.createMimeMessage();
+		MimeMessageHelper helper = new MimeMessageHelper(msg, "UTF-8");
+		try {
+			helper.setFrom(mailSenderName + "<" + mailSenderAddress + ">");
+			helper.setTo(dbUser.getUsername());
+			helper.setSubject(subject);
+			helper.setText(body);
+
+			LOGGER.info("Sending mail \"{}\" from \"{}\" to \"{}\"", new Object[] {subject, msg.getFrom(), dbUser.getUsername()});
+			mailSender.send(msg);
+		} catch (MessagingException e) {
+			LOGGER.warn("Mail \"{}\" could not be sent: {}", subject, e);
+		} catch (MailException e) {
+			LOGGER.warn("Mail \"{}\" could not be sent: {}", subject, e);
+		}
+	}
 }
diff --git a/src/main/java/de/thm/arsnova/socket/ARSnovaSocketIOServer.java b/src/main/java/de/thm/arsnova/socket/ARSnovaSocketIOServer.java
index 389b03ea4c2af948e9bbab7cddd0b2c6d6597e82..9348cf97c0146e180d14905ad71317bcf9e1f07d 100644
--- a/src/main/java/de/thm/arsnova/socket/ARSnovaSocketIOServer.java
+++ b/src/main/java/de/thm/arsnova/socket/ARSnovaSocketIOServer.java
@@ -55,8 +55,6 @@ public class ARSnovaSocketIOServer {
 	private final Configuration config;
 	private SocketIOServer server;
 
-	private int lastActiveUserCount = 0;
-
 	public ARSnovaSocketIOServer() {
 		config = new Configuration();
 	}
@@ -99,26 +97,38 @@ public class ARSnovaSocketIOServer {
 		server.addEventListener("setFeedback", Feedback.class, new DataListener<Feedback>() {
 			@Override
 			public void onData(final SocketIOClient client, final Feedback data, final AckRequest ackSender) {
-				/**
-				 * do a check if user is in the session, for which he would give
-				 * a feedback
-				 */
 				final User u = userService.getUser2SocketId(client.getSessionId());
-				if (u == null || !userService.isUserInSession(u, data.getSessionkey())) {
-					return;
+				final String sessionKey = userService.getSessionForUser(u.getUsername());
+				LOGGER.debug("Feedback recieved: {}", new Object[] {u, sessionKey, data.getValue()});
+				if (null != sessionKey) {
+					feedbackService.saveFeedback(sessionKey, data.getValue(), u);
 				}
-				feedbackService.saveFeedback(data.getSessionkey(), data.getValue(), u);
 			}
 		});
 
 		server.addEventListener("setSession", Session.class, new DataListener<Session>() {
 			@Override
 			public void onData(final SocketIOClient client, final Session session, final AckRequest ackSender) {
-				sessionService.joinSession(session.getKeyword(), client.getSessionId());
-				/* active user count has to be sent to the client since the broadcast is
-				 * not always sent as long as the polling solution is active simultaneously */
-				reportActiveUserCountForSession(session.getKeyword());
-				reportSessionDataToClient(session.getKeyword(), client);
+				final User u = userService.getUser2SocketId(client.getSessionId());
+				if (null == u) {
+					LOGGER.info("Client {} requested to join session but is not mapped to a user", client.getSessionId());
+
+					return;
+				}
+				final String oldSessionKey = userService.getSessionForUser(u.getUsername());
+				if (session.getKeyword() == oldSessionKey) {
+					return;
+				}
+				if (null != oldSessionKey) {
+					reportActiveUserCountForSession(oldSessionKey);
+				}
+
+				if (null != sessionService.joinSession(session.getKeyword(), client.getSessionId())) {
+					/* active user count has to be sent to the client since the broadcast is
+					 * not always sent as long as the polling solution is active simultaneously */
+					reportActiveUserCountForSession(session.getKeyword());
+					reportSessionDataToClient(session.getKeyword(), client);
+				}
 			}
 		});
 
@@ -281,11 +291,7 @@ public class ARSnovaSocketIOServer {
 
 	public void reportActiveUserCountForSession(final String sessionKey) {
 		/* This check is needed as long as the HTTP polling solution is active simultaneously. */
-		final int count = sessionService.activeUsers(sessionKey);
-		if (count == lastActiveUserCount) {
-			return;
-		}
-		lastActiveUserCount = count;
+		final int count = userService.getUsersInSession(sessionKey).size();
 
 		broadcastInSession(sessionKey, "activeUserCountData", count);
 	}
diff --git a/src/main/java/de/thm/arsnova/socket/message/Feedback.java b/src/main/java/de/thm/arsnova/socket/message/Feedback.java
index 8fdff90a09aec0d8945d99a793c6520b9b014831..2168e8d59574e1d3187dbf638f4bffcd19b3ccf1 100644
--- a/src/main/java/de/thm/arsnova/socket/message/Feedback.java
+++ b/src/main/java/de/thm/arsnova/socket/message/Feedback.java
@@ -3,15 +3,6 @@ package de.thm.arsnova.socket.message;
 public class Feedback {
 
 	private int value;
-	private String sessionkey;
-
-	public String getSessionkey() {
-		return sessionkey;
-	}
-
-	public void setSessionkey(String keyword) {
-		this.sessionkey = keyword;
-	}
 
 	public int getValue() {
 		return value;
@@ -23,6 +14,6 @@ public class Feedback {
 
 	@Override
 	public String toString() {
-		return "Feedback, sessionkey: " + sessionkey + ", value: " + value;
+		return "Feedback, value: " + value;
 	}
 }
diff --git a/src/main/resources/test.ldif b/src/main/resources/test.ldif
new file mode 100644
index 0000000000000000000000000000000000000000..54bc2b1fbbf12326ce0a581f33b83524600afaf6
--- /dev/null
+++ b/src/main/resources/test.ldif
@@ -0,0 +1,26 @@
+dn: dc=example, dc=com
+objectclass: organization
+objectclass: top
+o: Dummy Organisation
+
+dn: ou=people, dc=example, dc=com
+objectclass: organizationalunit
+ou: people
+
+dn: uid=ptsr00, ou=people, dc=example, dc=com
+objectclass: person
+objectclass: organizationalperson
+objectclass: inetorgperson
+uid: ptsr00
+sn: Tester
+givenName: Patrick
+userPassword:: VGVzdA==
+
+dn: uid=ptsr01, ou=people, dc=example, dc=com
+objectclass: person
+objectclass: organizationalperson
+objectclass: inetorgperson
+uid: ptsr01
+sn: Tester
+givenName: Patricia
+userPassword:: VGVzdA==
diff --git a/src/main/webapp/WEB-INF/spring/arsnova-servlet.xml b/src/main/webapp/WEB-INF/spring/arsnova-servlet.xml
index c06457d5453f8ad978b86f2346c2428a93d2e76d..1dc289bf8d65a31be351a0f1609a250039b5ae22 100644
--- a/src/main/webapp/WEB-INF/spring/arsnova-servlet.xml
+++ b/src/main/webapp/WEB-INF/spring/arsnova-servlet.xml
@@ -1,7 +1,10 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <beans xmlns="http://www.springframework.org/schema/beans"
-	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
-	xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context"
+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xmlns:aop="http://www.springframework.org/schema/aop"
+	xmlns:mvc="http://www.springframework.org/schema/mvc"
+	xmlns:context="http://www.springframework.org/schema/context"
+	xmlns:p="http://www.springframework.org/schema/p"
 	xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
 		http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
 		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
@@ -10,6 +13,7 @@
 	<!-- ARSnova Servlet Context -->
 
 	<context:component-scan base-package="de.thm.arsnova.controller,de.thm.arsnova.web" />
+	<context:property-placeholder location="file:///etc/arsnova/arsnova.properties" file-encoding="UTF-8" />
 	<mvc:annotation-driven
 		content-negotiation-manager="contentNegotiationManager" />
 
diff --git a/src/main/webapp/WEB-INF/spring/spring-main.xml b/src/main/webapp/WEB-INF/spring/spring-main.xml
index d39f43aae035f3e195f8f3f48bad878d44f3ccee..c94ed0de36aafc55edaee52ebc5d97e7b5ab0137 100644
--- a/src/main/webapp/WEB-INF/spring/spring-main.xml
+++ b/src/main/webapp/WEB-INF/spring/spring-main.xml
@@ -20,10 +20,13 @@
 				<value>file:///etc/arsnova/arsnova.properties</value>
 			</list>
 		</property>
+		<property name="fileEncoding" value="UTF-8" />
 	</bean>
 
-	<context:component-scan
-		base-package="de.thm.arsnova.dao,de.thm.arsnova.services,de.thm.arsnova.events,de.thm.arsnova.config" />
+	<import resource="spring-security.xml" />
+
+	<context:component-scan base-package="de.thm.arsnova.dao,de.thm.arsnova.events,de.thm.arsnova.security,de.thm.arsnova.services,de.thm.arsnova.config" />
+
 	<context:annotation-config />
 
 	<task:annotation-driven />
@@ -34,7 +37,9 @@
 
 	<bean id="userSessionAspect" class="de.thm.arsnova.aop.UserSessionAspect" />
 
-	<bean id="userService" scope="singleton" class="de.thm.arsnova.services.UserService" />
+	<bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
+		<property name="host" value="${mail.host}"/>
+	</bean>
 
 	<bean id="databaseDao" class="de.thm.arsnova.dao.CouchDBDao" />
 </beans>
diff --git a/src/main/webapp/WEB-INF/spring/spring-security.xml b/src/main/webapp/WEB-INF/spring/spring-security.xml
index 8c32335b1d358d4a89f04b2a4237d3cdf44d6520..0d843b17bc58c460b7ee4aab2094b2f92ec9b302 100644
--- a/src/main/webapp/WEB-INF/spring/spring-security.xml
+++ b/src/main/webapp/WEB-INF/spring/spring-security.xml
@@ -1,94 +1,99 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <beans xmlns="http://www.springframework.org/schema/beans"
-	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
-	xmlns:p="http://www.springframework.org/schema/p" xmlns:security="http://www.springframework.org/schema/security"
+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xmlns:security="http://www.springframework.org/schema/security"
+	xmlns:context="http://www.springframework.org/schema/context"
+	xmlns:p="http://www.springframework.org/schema/p"
 	xsi:schemaLocation="http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.2.xsd
 		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
 		http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd
 		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
 
 	<security:authentication-manager alias="authenticationManager">
-		<security:authentication-provider
-			ref="facebookAuthProvider" />
-		<security:authentication-provider
-			ref="twitterAuthProvider" />
-		<security:authentication-provider
-			ref="googleAuthProvider" />
-		<security:authentication-provider
-			ref="casAuthenticationProvider" />
+		<!-- <security:ldap-authentication-provider
+			user-search-filter="${security.ldap.user-search-filter}"
+			user-search-base="${security.ldap.user-search-base}" />
+			-->
+		<security:ldap-authentication-provider user-dn-pattern="${security.ldap.user-dn-pattern}" />
+		<security:authentication-provider ref="facebookAuthProvider" />
+		<security:authentication-provider ref="twitterAuthProvider" />
+		<security:authentication-provider ref="googleAuthProvider" />
+		<security:authentication-provider ref="casAuthenticationProvider" />
+		<security:authentication-provider ref="daoAuthenticationProvider" />
 	</security:authentication-manager>
 
-	<security:http entry-point-ref="facebookEntryPoint">
-		<security:custom-filter ref="facebookFilter"
-			before="CAS_FILTER" />
-		<security:custom-filter ref="twitterFilter"
-			after="CAS_FILTER" />
-		<security:custom-filter ref="googleFilter"
-			before="FORM_LOGIN_FILTER" />
-
-		<security:custom-filter ref="casAuthenticationFilter"
-			position="CAS_FILTER" />
-		<security:custom-filter ref="requestSingleLogoutFilter"
-			before="LOGOUT_FILTER" />
+	<security:http entry-point-ref="restLoginEntryPoint">
+		<security:custom-filter ref="facebookFilter" before="CAS_FILTER" />
+		<security:custom-filter ref="twitterFilter" after="CAS_FILTER" />
+		<security:custom-filter ref="googleFilter" before="FORM_LOGIN_FILTER" />
+		<security:custom-filter ref="casAuthenticationFilter" position="CAS_FILTER" />
+		<security:custom-filter ref="requestSingleLogoutFilter" before="LOGOUT_FILTER" />
 	</security:http>
 
-	<security:global-method-security
-		pre-post-annotations="enabled">
-		<security:expression-handler ref="expressionHandler" />
-
-	</security:global-method-security>
-
-	<bean id="permissionEvaluator" class="de.thm.arsnova.security.ApplicationPermissionEvaluator" />
-
-	<bean id="expressionHandler"
-		class="org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler">
-		<property name="permissionEvaluator" ref="permissionEvaluator" />
+	<!-- ######################### DB auth ############################# -->
+	<bean id="daoAuthenticationProvider"
+			class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
+		<property name="userDetailsService" ref="dbUserDetailsService" />
+		<property name="passwordEncoder" ref="passwordEncoder" />
+	</bean>
+	
+	<bean id="passwordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder" />
+
+	<!-- ######################### LDAP ############################# -->
+	<security:ldap-server url="${security.ldap.url}" />
+	<!-- <security:ldap-server ldif="classpath:/test.ldif" root="dc=example,dc=com" /> -->
+	
+	<bean id="restLoginEntryPoint" class="org.springframework.security.web.authentication.Http403ForbiddenEntryPoint" />
+
+	<bean id="loginUrlAuthenticationEntryPoint"
+		class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
+		<property name="loginFormUrl" value="/login.html" />
+		<property name="forceHttps" value="false" />
 	</bean>
 
 	<!-- ######################### FACEBOOK ######################### -->
-	<bean id="facebookEntryPoint"
-		class="com.github.leleuj.ss.oauth.client.web.OAuthAuthenticationEntryPoint"
+	<bean id="facebookEntryPoint" class="com.github.leleuj.ss.oauth.client.web.OAuthAuthenticationEntryPoint"
 		p:provider-ref="facebookProvider" />
 
 	<bean id="facebookProvider" class="org.scribe.up.provider.impl.FacebookProvider"
-		p:key="${security.facebook.key}" p:secret="${security.facebook.secret}"
-		p:callbackUrl="${security.arsnova-url}/j_spring_facebook_security_check" />
+		p:key="${security.facebook.key}"
+		p:secret="${security.facebook.secret}"
+		p:callbackUrl="${root-url}#{servletContext.contextPath}/j_spring_facebook_security_check" />
 
-	<bean id="facebookFilter"
-		class="com.github.leleuj.ss.oauth.client.web.OAuthAuthenticationFilter"
+	<bean id="facebookFilter" class="com.github.leleuj.ss.oauth.client.web.OAuthAuthenticationFilter"
 		p:filterProcessesUrl="/j_spring_facebook_security_check"
-		p:provider-ref="facebookProvider" p:authenticationManager-ref="authenticationManager"
+		p:provider-ref="facebookProvider"
+		p:authenticationManager-ref="authenticationManager"
 		p:authenticationFailureHandler-ref="failureHandler"
 		p:authenticationSuccessHandler-ref="successHandler" />
 
-	<bean id="facebookAuthProvider"
-		class="com.github.leleuj.ss.oauth.client.authentication.OAuthAuthenticationProvider"
+	<bean id="facebookAuthProvider" class="com.github.leleuj.ss.oauth.client.authentication.OAuthAuthenticationProvider"
 		p:provider-ref="facebookProvider" />
 
 	<!-- ######################### TWITTER ######################### -->
 	<bean id="twitterProvider" class="org.scribe.up.provider.impl.TwitterProvider"
-		p:key="${security.twitter.key}" p:secret="${security.twitter.secret}"
-		p:callbackUrl="${security.arsnova-url}/j_spring_twitter_security_check" />
+		p:key="${security.twitter.key}"
+		p:secret="${security.twitter.secret}"
+		p:callbackUrl="${root-url}#{servletContext.contextPath}/j_spring_twitter_security_check" />
 
-	<bean id="twitterFilter"
-		class="com.github.leleuj.ss.oauth.client.web.OAuthAuthenticationFilter"
+	<bean id="twitterFilter" class="com.github.leleuj.ss.oauth.client.web.OAuthAuthenticationFilter"
 		p:filterProcessesUrl="/j_spring_twitter_security_check"
-		p:provider-ref="twitterProvider" p:authenticationManager-ref="authenticationManager"
+		p:provider-ref="twitterProvider"
+		p:authenticationManager-ref="authenticationManager"
 		p:authenticationFailureHandler-ref="failureHandler"
 		p:authenticationSuccessHandler-ref="successHandler" />
 
-	<bean id="twitterAuthProvider"
-		class="com.github.leleuj.ss.oauth.client.authentication.OAuthAuthenticationProvider"
+	<bean id="twitterAuthProvider" class="com.github.leleuj.ss.oauth.client.authentication.OAuthAuthenticationProvider"
 		p:provider-ref="twitterProvider" />
 
 	<!-- ######################### GOOGLE ######################### -->
 	<bean id="googleProvider" class="org.scribe.up.provider.impl.Google2Provider"
-		p:key="${security.google.key}" p:secret="${security.google.secret}"
-		p:scope-ref="googleScope" p:callbackUrl="${security.arsnova-url}/j_spring_google_security_check" />
-
-	<bean id="googleScope"
-		class="org.scribe.up.provider.impl.Google2Provider.Google2Scope"
-		factory-method="valueOf">
+		p:key="${security.google.key}"
+		p:secret="${security.google.secret}"
+		p:scope-ref="googleScope"
+		p:callbackUrl="${root-url}#{servletContext.contextPath}/j_spring_google_security_check" />
+	
+	<bean id="googleScope" class="org.scribe.up.provider.impl.Google2Provider.Google2Scope" factory-method="valueOf">
 		<constructor-arg index="0" value="EMAIL" />
 	</bean>
 
@@ -112,10 +117,12 @@
 
 	<bean id="casEntryPoint"
 		class="org.springframework.security.cas.web.CasAuthenticationEntryPoint"
-		p:loginUrl="${security.cas-server-url}/login" p:serviceProperties-ref="casServiceProperties" />
-
-	<bean id="casServiceProperties" class="org.springframework.security.cas.ServiceProperties"
-		p:service="${security.arsnova-url}/j_spring_cas_security_check"
+		p:loginUrl="${security.cas-server-url}/login"
+		p:serviceProperties-ref="casServiceProperties" />
+	
+	<bean id="casServiceProperties" 
+		class="org.springframework.security.cas.ServiceProperties"
+		p:service="${root-url}#{servletContext.contextPath}/j_spring_cas_security_check"
 		p:sendRenew="false" />
 
 	<bean id="casAuthenticationProvider"
@@ -141,13 +148,14 @@
 	</bean>
 
 	<bean id="casLogoutSuccessHandler" class="de.thm.arsnova.CASLogoutSuccessHandler"
-		p:casUrl="${security.cas-server-url}" p:defaultTarget="${security.arsnova-url}" />
-
+		p:casUrl="${security.cas-server-url}"
+		p:defaultTarget="${root-url}"/>
+	
 	<bean id="successHandler" class="de.thm.arsnova.LoginAuthenticationSucessHandler"
-		p:targetUrl="#auth/checkLogin" />
+		p:targetUrl="${root-url}"/>
 
 	<bean id="failureHandler" class="de.thm.arsnova.LoginAuthenticationFailureHandler"
-		p:defaultFailureUrl="/index.html" />
+		p:defaultFailureUrl="${root-url}" />
 
 	<!-- Session Registry -->
 	<bean id="sessionRegistry"
diff --git a/src/main/webapp/arsnova.properties.example b/src/main/webapp/arsnova.properties.example
index e92e71bf6b7326eac6ad958b634304ee741472dc..0fa0a198ebe3ad3123f24c8a8a8b824f899462bd 100644
--- a/src/main/webapp/arsnova.properties.example
+++ b/src/main/webapp/arsnova.properties.example
@@ -1,35 +1,199 @@
-security.arsnova-url=http://localhost:8080
-security.cas-server-url=https://cas.thm.de/cas
+################################################################################
+# General server settings
+################################################################################
+# The URL under which the ARSnova server is reachable. Use
+# http://localhost:8080 for development.
+root-url=https://example.com
 
-security.facebook.key=318531508227494
-security.facebook.secret=e3f38cfc72bb63e35641b637081a6177
-
-security.twitter.key=PEVtidSG0HzSrxVRPpsCXw
-security.twitter.secret=mC0HOvxiEgqwdDWCcDoy3q75nUQPu1bYRp1ncHWGd0
-
-security.google.key=110959746118.apps.googleusercontent.com
-security.google.secret=CkzUJZswY8rjWCCYnHVovyGA
+# The context paths where the ARSnova modules have been deployed
+customization.path=/customization
+mobile.path=/mobile
+presenter.path=/presenter
 
+# SSL configuration
 security.ssl=false
-security.keystore=/etc/arsnova.thm.de.jks
+security.keystore=/etc/arsnova/arsnova.jks
 security.storepass=arsnova
 
-# minutes, after which the feedback is deleted
-feedback.cleanup=10
+# WebSockets server
+socketio.ip=0.0.0.0
+socketio.port=10443
 
-# maximal filesize in bytes
-upload.filesize_b=1048576
 
+################################################################################
+# Database
+################################################################################
 couchdb.host=localhost
 couchdb.port=5984
 couchdb.name=arsnova
 couchdb.username=admin
 couchdb.password=
 
-socketio.ip=0.0.0.0
-socketio.port=10443
 
+################################################################################
+# E-Mail
+################################################################################
+mail.sender.name=ARSnova
+mail.sender.address=
+mail.host=
+
+
+################################################################################
+# Authentication
+################################################################################
+# After the specified number of login tries the client IP will be banned for
+# several minutes
+security.authentication.login-try-limit=50
+
+# Configuration parameters for authentication services:
+# enabled: enable or disable the service
+# title: the title which is displayed by frontends
+# login-dialog-path: URL of a login dialog page
+# image: an image which is used for frontend buttons
+
+# Guest authentication
+#
+security.guest.enabled=true
+security.guest.order=0
+security.guest.lecturer.enabled=true
+
+# Setup combined login if you want to use a single, customized login page
+# which is used for multiple authentication services.
+#
+security.custom-login.enabled=false
+security.custom-login.title=University
+security.custom-login.login-dialog-path=
+security.custom-login.image=
+security.custom-login.order=0
+
+# Internal authentication
+#
+# Specific parameters:
+# activation-path: URL of the account activation page
+# allowed-email-domains: Allows you to restrict registration to specific
+#     domains. You can use wildcards (*), e. g. *.*.example.com. Multiple
+#     entries are separated by commas.
+# registration-mail.subject: Subject used for registration e-mail
+# registration-mail.body: Text body used for registration e-mail. {0} will be
+#     replaced by the value of activation-path.
+#
+security.user-db.enabled=true
+security.user-db.title=ARSnova
+security.user-db.login-dialog-path=account.html
+security.user-db.activation-path=account.html
+security.user-db.reset-password-path=account.html
+security.user-db.image=
+security.user-db.order=0
+security.user-db.allowed-email-domains=*
+security.user-db.registration-mail.subject=ARSnova Registration
+security.user-db.registration-mail.body=Welcome to ARSnova!\n\nPlease confirm \
+    your registration by visiting the following web address:\n{0}\n\n\
+    Afterwards, you can log into ARSnova with your e-mail address and password.
+security.user-db.reset-password-mail.subject=ARSnova Password Reset
+security.user-db.reset-password-mail.body=You requested to reset your \
+    password.\n\nPlease follow the link below to set a new password:\n{0}
+
+# LDAP authentication
+# 
+# Specific parameters:
+# url: LDAP server URL
+# user-dn-pattern: Pattern used to check user credentials against the LDAP
+#     server. {0} will be replaced with the user ID by ARSnova.
+#
+security.ldap.enabled=true
+security.ldap.title=LDAP
+security.ldap.login-dialog-path=login-ldap.html
+security.ldap.image=
+security.ldap.order=0
+security.ldap.url=ldap://example.com:33389/dc=example,dc=com
+security.ldap.user-dn-pattern=uid={0},ou=arsnova
+# Not yet implemented parameters
+#security.ldap.user-search-filter=(uid={0})
+#security.ldap.user-search-base="ou=people"
+
+# CAS authentication
+#
+security.cas.enabled=true
+security.cas.title=CAS
+security.cas.image=
+security.cas.order=0
+security.cas-server-url=https://example.com/cas
+
+# OAuth authentication with third party services
+# Specific parameters:
+# key: OAuth key/id provided by a third party auth service
+# secret: OAuth secret provided by a third party auth service
+
+# Facebook
+#
+security.facebook.enabled=true
+security.facebook.order=0
+security.facebook.key=
+security.facebook.secret=
+
+# Twitter
+#
+security.twitter.enabled=true
+security.twitter.order=0
+security.twitter.key=
+security.twitter.secret=
+
+# Google
+#
+security.google.enabled=true
+security.google.order=0
+security.google.key=
+security.google.secret=
+
+
+################################################################################
+# ARSnova Connector (for LMS)
+################################################################################
 connector.enable=false
 connector.uri=http://localhost:8080/connector-service
 connector.username=test
 connector.password=test
+
+
+################################################################################
+# Features
+################################################################################
+# Enable MathJax to allow the use of Math formulas written in TeX syntax in
+# text fields.
+features.mathjax.enabled=true
+
+# The following features are considered experimental because they have not been
+# tested in a production environment over a longer time frame and/or their
+# behavior will change in future releases.
+#
+features.markdown.enabled=false
+features.learning-progress.enabled=false
+features.question-format.flashcard.enabled=false
+features.question-format.grid-square.enabled=false
+
+
+################################################################################
+# Customization
+################################################################################
+# minutes, after which the feedback is deleted
+feedback.cleanup=10
+
+# maximal filesize in bytes
+upload.filesize_b=1048576
+
+# maximal number of answer options allowed for a skill question
+question.answer-option-limit=8
+
+# Enable Markdown and MathJax parsing in answer options. Formatting in answer
+# options should be used cautiously since it could lead to display errors.
+# Answer options will still not be parsed in diagrams. This setting has no
+# effect if neither MathJax nor Markdown are enabled.
+question.parse-answer-option-formatting=false
+
+# Links which are displayed in the frontend applications
+#
+links.documentation.url=https://arsnova.eu/manual/
+links.overlay.url=https://arsnova.eu/overlay/
+links.organization.url=
+links.imprint.url=
+links.privacy-policy.url=
diff --git a/src/test/java/de/thm/arsnova/dao/StubDatabaseDao.java b/src/test/java/de/thm/arsnova/dao/StubDatabaseDao.java
index c73b88f96c0560c885ed9865c881c3a530cd54eb..8dcafd979a2068b3220978605480cabd57c21586 100644
--- a/src/test/java/de/thm/arsnova/dao/StubDatabaseDao.java
+++ b/src/test/java/de/thm/arsnova/dao/StubDatabaseDao.java
@@ -26,6 +26,7 @@ import java.util.concurrent.ConcurrentHashMap;
 
 import de.thm.arsnova.connector.model.Course;
 import de.thm.arsnova.entities.Answer;
+import de.thm.arsnova.entities.DbUser;
 import de.thm.arsnova.entities.Feedback;
 import de.thm.arsnova.entities.FoodVote;
 import de.thm.arsnova.entities.InterposedQuestion;
@@ -500,6 +501,18 @@ public class StubDatabaseDao implements IDatabaseDao {
 
 	}
 
+	@Override
+	public DbUser createOrUpdateUser(DbUser user) {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	@Override
+	public DbUser getUser(String username) {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
 	@Override
 	public int getLearningProgress(Session session) {
 		// TODO Auto-generated method stub
@@ -511,4 +524,10 @@ public class StubDatabaseDao implements IDatabaseDao {
 		// TODO Auto-generated method stub
 		return null;
 	}
+
+	@Override
+	public boolean deleteUser(DbUser dbUser) {
+		// TODO Auto-generated method stub
+		return false;
+	}
 }
diff --git a/src/test/resources/arsnova.properties.example b/src/test/resources/arsnova.properties.example
index f5830dcb6d2a79fb1f5a9e5564e36a895a9ea5da..0fa0a198ebe3ad3123f24c8a8a8b824f899462bd 100644
--- a/src/test/resources/arsnova.properties.example
+++ b/src/test/resources/arsnova.properties.example
@@ -1,33 +1,199 @@
-security.arsnova-url=http://localhost:8080
-security.cas-server-url=https://cas.thm.de/cas
+################################################################################
+# General server settings
+################################################################################
+# The URL under which the ARSnova server is reachable. Use
+# http://localhost:8080 for development.
+root-url=https://example.com
 
-security.facebook.key=318531508227494
-security.facebook.secret=e3f38cfc72bb63e35641b637081a6177
-
-security.twitter.key=PEVtidSG0HzSrxVRPpsCXw
-security.twitter.secret=mC0HOvxiEgqwdDWCcDoy3q75nUQPu1bYRp1ncHWGd0
-
-security.google.key=110959746118.apps.googleusercontent.com
-security.google.secret=CkzUJZswY8rjWCCYnHVovyGA
+# The context paths where the ARSnova modules have been deployed
+customization.path=/customization
+mobile.path=/mobile
+presenter.path=/presenter
 
+# SSL configuration
 security.ssl=false
-security.keystore=/etc/arsnova.thm.de.jks
+security.keystore=/etc/arsnova/arsnova.jks
 security.storepass=arsnova
 
+# WebSockets server
+socketio.ip=0.0.0.0
+socketio.port=10443
+
+
+################################################################################
+# Database
+################################################################################
 couchdb.host=localhost
 couchdb.port=5984
 couchdb.name=arsnova
+couchdb.username=admin
+couchdb.password=
 
-# minutes, after which the feedback is deleted
-feedback.cleanup=10
 
-# maximal filesize in bytes
-upload.filesize_b=1048576
+################################################################################
+# E-Mail
+################################################################################
+mail.sender.name=ARSnova
+mail.sender.address=
+mail.host=
 
-socketio.ip=0.0.0.0
-socketio.port=10443
 
+################################################################################
+# Authentication
+################################################################################
+# After the specified number of login tries the client IP will be banned for
+# several minutes
+security.authentication.login-try-limit=50
+
+# Configuration parameters for authentication services:
+# enabled: enable or disable the service
+# title: the title which is displayed by frontends
+# login-dialog-path: URL of a login dialog page
+# image: an image which is used for frontend buttons
+
+# Guest authentication
+#
+security.guest.enabled=true
+security.guest.order=0
+security.guest.lecturer.enabled=true
+
+# Setup combined login if you want to use a single, customized login page
+# which is used for multiple authentication services.
+#
+security.custom-login.enabled=false
+security.custom-login.title=University
+security.custom-login.login-dialog-path=
+security.custom-login.image=
+security.custom-login.order=0
+
+# Internal authentication
+#
+# Specific parameters:
+# activation-path: URL of the account activation page
+# allowed-email-domains: Allows you to restrict registration to specific
+#     domains. You can use wildcards (*), e. g. *.*.example.com. Multiple
+#     entries are separated by commas.
+# registration-mail.subject: Subject used for registration e-mail
+# registration-mail.body: Text body used for registration e-mail. {0} will be
+#     replaced by the value of activation-path.
+#
+security.user-db.enabled=true
+security.user-db.title=ARSnova
+security.user-db.login-dialog-path=account.html
+security.user-db.activation-path=account.html
+security.user-db.reset-password-path=account.html
+security.user-db.image=
+security.user-db.order=0
+security.user-db.allowed-email-domains=*
+security.user-db.registration-mail.subject=ARSnova Registration
+security.user-db.registration-mail.body=Welcome to ARSnova!\n\nPlease confirm \
+    your registration by visiting the following web address:\n{0}\n\n\
+    Afterwards, you can log into ARSnova with your e-mail address and password.
+security.user-db.reset-password-mail.subject=ARSnova Password Reset
+security.user-db.reset-password-mail.body=You requested to reset your \
+    password.\n\nPlease follow the link below to set a new password:\n{0}
+
+# LDAP authentication
+# 
+# Specific parameters:
+# url: LDAP server URL
+# user-dn-pattern: Pattern used to check user credentials against the LDAP
+#     server. {0} will be replaced with the user ID by ARSnova.
+#
+security.ldap.enabled=true
+security.ldap.title=LDAP
+security.ldap.login-dialog-path=login-ldap.html
+security.ldap.image=
+security.ldap.order=0
+security.ldap.url=ldap://example.com:33389/dc=example,dc=com
+security.ldap.user-dn-pattern=uid={0},ou=arsnova
+# Not yet implemented parameters
+#security.ldap.user-search-filter=(uid={0})
+#security.ldap.user-search-base="ou=people"
+
+# CAS authentication
+#
+security.cas.enabled=true
+security.cas.title=CAS
+security.cas.image=
+security.cas.order=0
+security.cas-server-url=https://example.com/cas
+
+# OAuth authentication with third party services
+# Specific parameters:
+# key: OAuth key/id provided by a third party auth service
+# secret: OAuth secret provided by a third party auth service
+
+# Facebook
+#
+security.facebook.enabled=true
+security.facebook.order=0
+security.facebook.key=
+security.facebook.secret=
+
+# Twitter
+#
+security.twitter.enabled=true
+security.twitter.order=0
+security.twitter.key=
+security.twitter.secret=
+
+# Google
+#
+security.google.enabled=true
+security.google.order=0
+security.google.key=
+security.google.secret=
+
+
+################################################################################
+# ARSnova Connector (for LMS)
+################################################################################
 connector.enable=false
 connector.uri=http://localhost:8080/connector-service
 connector.username=test
 connector.password=test
+
+
+################################################################################
+# Features
+################################################################################
+# Enable MathJax to allow the use of Math formulas written in TeX syntax in
+# text fields.
+features.mathjax.enabled=true
+
+# The following features are considered experimental because they have not been
+# tested in a production environment over a longer time frame and/or their
+# behavior will change in future releases.
+#
+features.markdown.enabled=false
+features.learning-progress.enabled=false
+features.question-format.flashcard.enabled=false
+features.question-format.grid-square.enabled=false
+
+
+################################################################################
+# Customization
+################################################################################
+# minutes, after which the feedback is deleted
+feedback.cleanup=10
+
+# maximal filesize in bytes
+upload.filesize_b=1048576
+
+# maximal number of answer options allowed for a skill question
+question.answer-option-limit=8
+
+# Enable Markdown and MathJax parsing in answer options. Formatting in answer
+# options should be used cautiously since it could lead to display errors.
+# Answer options will still not be parsed in diagrams. This setting has no
+# effect if neither MathJax nor Markdown are enabled.
+question.parse-answer-option-formatting=false
+
+# Links which are displayed in the frontend applications
+#
+links.documentation.url=https://arsnova.eu/manual/
+links.overlay.url=https://arsnova.eu/overlay/
+links.organization.url=
+links.imprint.url=
+links.privacy-policy.url=
diff --git a/src/test/resources/test-config.xml b/src/test/resources/test-config.xml
index 91833913381472c84ee33c392050884ac7765427..1da5cc9d310d3894a29db0740ee8842e93a2d000 100644
--- a/src/test/resources/test-config.xml
+++ b/src/test/resources/test-config.xml
@@ -8,6 +8,10 @@
 		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
 		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
 
+	<bean id="servletContext" class="org.springframework.mock.web.MockServletContext">
+		<property name="contextPath" value="/" />
+	</bean>
+
 	<bean id="sessionService" class="de.thm.arsnova.services.SessionService">
 		<property name="databaseDao" ref="databaseDao" />
 	</bean>
diff --git a/src/test/resources/test.ldif b/src/test/resources/test.ldif
new file mode 100644
index 0000000000000000000000000000000000000000..f0a60c365b38d3589649a507818bac4b8701500d
--- /dev/null
+++ b/src/test/resources/test.ldif
@@ -0,0 +1,22 @@
+dn: dc=example, dc=com
+objectclass: organization
+objectclass: top
+o: Dummy Organisation
+
+dn: uid=ptsr00, dc=example, dc=com
+objectclass: person
+objectclass: organizationalperson
+objectclass: inetorgperson
+uid: ptsr00
+sn: Tester
+givenName: Patrick
+userPassword:: VGVzdA==
+
+dn: uid=ptsr01, dc=example, dc=com
+objectclass: person
+objectclass: organizationalperson
+objectclass: inetorgperson
+uid: ptsr01
+sn: Tester
+givenName: Patricia
+userPassword:: VGVzdA==