diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/CommonServer.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/CommonServer.java
index da43cb1ebe714c397e7e26633280c3ed4da1cd4c..29964eaaf15113af4fe14598bc309dda0a9eca52 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/CommonServer.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/CommonServer.java
@@ -528,7 +528,7 @@ public final class CommonServer extends AbstractCommonServer<ICommonServerForInt
     }
 
     @Override
-    @RolesAllowed(RoleWithHierarchy.INSTANCE_ADMIN)
+    @RolesAllowed(RoleWithHierarchy.INSTANCE_ETL_SERVER)
     public List<Person> listPersons(String sessionToken)
     {
         checkSession(sessionToken);
diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/controllers/MainController.js b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/controllers/MainController.js
index e8a4a0449357a11a5c5f988c41c1b58e0247e83e..75ba7362cd42cb260e24ae00a3936823d7cfdd48 100644
--- a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/controllers/MainController.js
+++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/controllers/MainController.js
@@ -37,22 +37,14 @@ function MainController(profile) {
 	
 	this.openbisV1 = new openbis();
 	this.openbisV3 = null;
-	
-	
+
+
 	this.loadV3 = function(callback) {
-		require(['openbis'], function(openbis) {
-			//Boilerplate
-			var testProtocol = window.location.protocol;
-			var testHost = window.location.hostname;
-			var testPort = window.location.port;
-			
-			var testUrl = testProtocol + "//" + testHost + ":" + testPort;
-			var testApiUrl = testUrl + "/openbis/openbis/rmi-application-server-v3.json";
-			
-			mainController.openbisV3 = new openbis(testApiUrl);
-			mainController.openbisV3._private.sessionToken = mainController.serverFacade.getSession();
-			callback();
-		});
+	    this.serverFacade.getOpenbisV3(function(openbisV3) {
+            mainController.openbisV3 = openbisV3
+            mainController.openbisV3._private.sessionToken = mainController.serverFacade.getSession();
+            callback();
+	    });
 	};
 	
 	// Server Facade Object
@@ -100,9 +92,12 @@ function MainController(profile) {
 			$("#username").focus();
 			var callback = function() {Util.unblockUI();};
 			Util.showError('The given username or password is not correct.', callback);
+			this.serverFacade.doIfFileAuthenticationService((function() {
+	            this._enablePasswordResetLink();
+			}).bind(this));
 			return;
 		}
-		
+
 		//
 		// Back Button Logic
 		//
@@ -207,7 +202,50 @@ function MainController(profile) {
 				);
 		});
 	}
-	
+
+	this._enablePasswordResetLink = function() {
+
+        var userId = $("#username").val();
+	    var $container = $("#password-reset-container");
+	    $container.empty();
+
+	    $resetLink = $("<a>").text("reset password by email for user " + userId);
+	    $container.append($resetLink);
+	    var height = $container.height();
+	    $container.css({ "margin-top" : -height + "px" });
+
+	    $resetLink.on("click", (function() {
+	        Util.blockUI();
+    	    this.serverFacade.sendResetPasswordEmail(userId, function() {
+                Util.unblockUI();
+                Util.showInfo("An email with instructions how to reset the password has been sent to " + userId + " if this user exists.");
+    	    });
+
+	    }).bind(this));
+
+	}
+
+	this.resetPasswordRequested = function() {
+        var queryString = Util.queryString();
+        return queryString.resetPassword == "true";
+	}
+
+	this.resetPassword = function() {
+        var queryString = Util.queryString();
+        var userId = queryString.userId;
+        var token = queryString.token;
+
+        if (userId && token) {
+            Util.blockUI();
+            this.serverFacade.resetPassword(userId, token, function() {
+                Util.unblockUI();
+                Util.showInfo("A new password has been sent as an email to user " + userId + " if this user exists.");                
+            });
+        } else {
+            Util.showError("To reset the password, the parameters 'userId' and 'token' need to be set.");
+        }
+	}
+
 	//
 	// Main View Changer - Everything on the application rely on this method to alter the views, arg should be a string
 	//
diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/server/ServerFacade.js b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/server/ServerFacade.js
index dc293a163d596794fe4898a0df8025efbf07ddaf..9b942f6316d8db0fc9d65f9e78d2f40ea0c49de4 100644
--- a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/server/ServerFacade.js
+++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/server/ServerFacade.js
@@ -26,8 +26,26 @@
  */
 function ServerFacade(openbisServer) {
 	this.openbisServer = openbisServer;
-	
+
+	//
+	// V3 API creation
 	//
+    this.getOpenbisV3 = function(callbackFunction) {
+        require(['openbis'], function(openbis) {
+            //Boilerplate
+            var testProtocol = window.location.protocol;
+            var testHost = window.location.hostname;
+            var testPort = window.location.port;
+
+            var testUrl = testProtocol + "//" + testHost + ":" + testPort;
+            var testApiUrl = testUrl + "/openbis/openbis/rmi-application-server-v3.json";
+
+            var openbisV3 = new openbis(testApiUrl);
+            callbackFunction(openbisV3);
+        });
+    }
+
+    //
 	// Intercepting general errors
 	//
 	var responseInterceptor = function(response, action){
@@ -560,6 +578,66 @@ function ServerFacade(openbisServer) {
 	//
 	// ELN Custom API
  	//
+
+    this.sendResetPasswordEmail = function(userId, callbackFunction) {
+        var parameters = {
+                method : "sendResetPasswordEmail",
+                userId : userId,
+                baseUrl : location.protocol + '//' + location.host + location.pathname
+        };
+        this._callPasswordResetService(parameters, callbackFunction);
+    }
+
+    this.resetPassword = function(userId, token, callbackFunction) {
+        var parameters = {
+                method : "resetPassword",
+                userId : userId,
+                token : token
+            };
+        this._callPasswordResetService(parameters, callbackFunction);        
+    }
+
+    this.doIfFileAuthenticationService = function(callbackFunction) {
+        var _this = this;
+        this.getOpenbisV3(function(openbisV3) {
+            openbisV3.loginAsAnonymousUser().done(function(sessionToken) {
+                openbisV3.getServerInformation().done(function(serverInformation) {
+                    var authSystem = serverInformation["authentication-service"];
+                    if (authSystem && authSystem.indexOf("file") !== -1) {
+                        callbackFunction();
+                    }
+                });
+            }).fail(function(result) {
+                console.log("Call failed to server: " + JSON.stringify(result));
+            });
+        });
+    }
+
+    this._callPasswordResetService = function(parameters, callbackFunction) {
+        var _this = this;
+        this.getOpenbisV3(function(openbisV3) {
+
+            openbisV3.loginAsAnonymousUser().done(function(sessionToken) {
+                _this.openbisServer._internal.sessionToken = sessionToken;
+
+                _this.listDataStores(function(dataStores) {
+                    profile.allDataStores = dataStores.result;
+                    _this.customELNApi(parameters, function(error, result) {
+                        if (error) {
+                            Util.showError(error);
+                        } else {
+                            callbackFunction(result);                            
+                        }
+                    }, "password-reset-api");
+                });
+
+            }).fail(function(result) {
+                console.log("Call failed to server: " + JSON.stringify(result));
+            });
+
+        });
+    }
+
  	this.customELNApi = function(parameters, callbackFunction, service) {
  		if(!service) {
  			service = "eln-lims-api";
diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/password-reset-api/lib/persistentkeyvaluestore-source/PersistentKeyValueStore.java b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/password-reset-api/lib/persistentkeyvaluestore-source/PersistentKeyValueStore.java
new file mode 100644
index 0000000000000000000000000000000000000000..c1aec05d6bbd423904e249ee5c068aa4ce0371ba
--- /dev/null
+++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/password-reset-api/lib/persistentkeyvaluestore-source/PersistentKeyValueStore.java
@@ -0,0 +1,27 @@
+package ch.ethz.sis;
+
+import java.io.Serializable;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+
+
+public class PersistentKeyValueStore {
+	private static ConcurrentMap<String, Serializable> keyStore = new ConcurrentHashMap<>();
+	
+	public static void put(String key, Serializable value) {
+		keyStore.put(key, value);
+	}
+	
+	public static Serializable get(String key) {
+		return keyStore.get(key);
+	}
+	
+	public static void remove(String key) {
+		keyStore.remove(key);
+	}
+	
+	public static boolean containsKey(String key) {
+		return keyStore.containsKey(key);
+	}
+}
diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/password-reset-api/lib/persistentkeyvaluestore.jar b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/password-reset-api/lib/persistentkeyvaluestore.jar
new file mode 100644
index 0000000000000000000000000000000000000000..d3f77f6a23766b0947ddd07d80311cdfe1069101
Binary files /dev/null and b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/password-reset-api/lib/persistentkeyvaluestore.jar differ
diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/password-reset-api/password-reset-api.py b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/password-reset-api/password-reset-api.py
new file mode 100644
index 0000000000000000000000000000000000000000..486988df762a7fdf8297c09894d403c9566a9651
--- /dev/null
+++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/password-reset-api/password-reset-api.py
@@ -0,0 +1,130 @@
+from ch.systemsx.cisd.openbis.dss.generic.server import DataStoreServer
+from ch.systemsx.cisd.openbis.generic.shared.api.v1 import IGeneralInformationService;
+from ch.systemsx.cisd.common.exceptions import UserFailureException
+from ch.ethz.sis import PersistentKeyValueStore
+from ch.ethz.sis.openbis.generic.server.sharedapi.v3.json import GenericObjectMapper;
+from ch.systemsx.cisd.openbis.common.api.client import ServiceFinder;
+from ch.systemsx.cisd.common.mail import EMailAddress;
+from java.lang import String
+from java.util import UUID
+from random import SystemRandom
+import subprocess, os, string, time
+
+
+passwdShPath = '../openBIS-server/jetty/bin/passwd.sh'
+RESET_TOKEN_KEY_POSTFIX = "-reset-token"
+OPENBISURL = DataStoreServer.getConfigParameters().getServerURL() + "/openbis/openbis"
+
+#
+# API functions
+#
+
+def process(tr, parameters, tableBuilder):
+    if (parameters["method"] == "sendResetPasswordEmail"):
+        if (parameters["userId"] is not None and parameters["baseUrl"] is not None):
+            sendResetPasswordEmail(tr, parameters["userId"], parameters["baseUrl"])
+        else:
+            raise UserFailureException("When invoking method 'sendResetPasswordEmail', the parameter 'userId' is required.")
+    elif (parameters["method"] == "resetPassword"):
+        if (parameters["userId"] is not None and parameters["token"] is not None):
+            resetPassword(tr, parameters["userId"], parameters["token"])
+        else:
+            raise UserFailureException("When invoking method 'resetPassword', the parameters 'userId' and 'token' are required.")
+    else:
+        raise UserFailureException("Unknown method: " + parameters["method"])
+    
+    tableBuilder.addHeader("STATUS");
+    tableBuilder.addHeader("MESSAGE");
+    tableBuilder.addHeader("RESULT");
+    row = tableBuilder.addRow();
+    row.setCell("STATUS","OK");
+    row.setCell("MESSAGE", "Operation Successful");
+    row.setCell("RESULT", getJsonForData( { "result" : "success" }));    
+
+
+def sendResetPasswordEmail(tr, userId, baseUrl):
+    print("sendResetPasswordEmail")
+    # generate and store token
+    token = UUID.randomUUID().toString()
+    timestamp = time.time()
+    print("timestamp: " + str(timestamp))
+    PersistentKeyValueStore.put(userId + RESET_TOKEN_KEY_POSTFIX, { "token" : token, "timestamp" : timestamp})
+    # send email
+    emailAddress = getUserEmail(tr, userId)
+    sendResetPasswordEmailInternal(tr, emailAddress, userId, token, baseUrl)
+
+def resetPassword(tr, userId, token):
+    if tokenIsValid(userId, token):
+        email = getUserEmail(tr, userId)
+        resetPasswordInternal(tr, email, userId)
+        PersistentKeyValueStore.remove(userId + RESET_TOKEN_KEY_POSTFIX)
+    else:
+        raise UserFailureException("Invalid token.")
+
+#
+# internal functions
+#
+
+def sendMail(tr, email, subject, body):
+    replyTo = None;
+    fromAddress = None;
+    recipient1 = EMailAddress(email);
+    tr.getGlobalState().getMailClient().sendEmailMessage(subject, body, replyTo, fromAddress, recipient1);
+    # TODO don't print message - contains password
+    print "--- MAIL ---" + " Recipient: " + email + " Topic: " + subject + " Message: " + body
+
+def getJsonForData(data):
+    objectMapper = GenericObjectMapper();
+    jsonValue = objectMapper.writeValueAsString(data);
+    return jsonValue;
+
+def tokenIsValid(userId, token):
+    return PersistentKeyValueStore.get(userId + RESET_TOKEN_KEY_POSTFIX) == token
+
+def sendResetPasswordEmailInternal(tr, email, userId, token, baseUrl):
+    passwordResetLink = getPasswordResetLink(email, userId, token, baseUrl)
+    passwordResetRequestSubject = getProperty(tr, "password-reset-request-subject") % (userId)
+    passwordResetRequestBody = getProperty(tr, "password-reset-request-body") % (userId, passwordResetLink)
+    sendMail(tr, email, passwordResetRequestSubject, passwordResetRequestBody)
+
+def sendEmailWithNewPassword(tr, email, userId, newPassword):
+    newPasswordSubject = getProperty(tr, "new-password-subject") % (userId)
+    newPasswordBody = getProperty(tr, "new-password-body") % (newPassword)
+    sendMail(tr, email, newPasswordSubject, newPasswordBody)
+
+def getPasswordResetLink(emailAddress, userId, token, baseUrl):
+    return "%s?resetPassword=true&&userId=%s&token=%s" % (baseUrl, userId, token)
+
+def resetPasswordInternal(tr, email, userId):
+    newPassword = getNewPassword()
+    updateUserPassword(userId, newPassword)
+    sendEmailWithNewPassword(tr, email, userId, newPassword)
+
+def getUserEmail(tr, userId):
+    for person in getPersons(tr):
+        if person.getUserId() == userId:
+            return person.getEmail()
+    raise UserFailureException("User email not found.")
+
+def getPersons(tr):
+    servFinder = ServiceFinder("openbis", IGeneralInformationService.SERVICE_URL);
+    infService = servFinder.createService(IGeneralInformationService, OPENBISURL);
+    return infService.listPersons(tr.getOpenBisServiceSessionToken());
+
+def getNewPassword():
+    length = 12
+    rng = SystemRandom()
+    chars = string.ascii_letters + string.digits + '!@#$%^&*()'
+    return ''.join(rng.choice(chars) for i in range(length))
+
+def updateUserPassword(userId, password):
+    if os.path.isfile(passwdShPath):
+        subprocess.call([path, 'change', userId, '-p', password]) #Changes the user pass, works always
+        return True;
+    else:
+        return False;
+
+def getProperty(tr, key):
+    threadPropertyDict = {}
+    threadProperties = tr.getGlobalState().getThreadParameters().getThreadProperties()
+    return threadProperties.getProperty(key)
diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/password-reset-api/plugin.properties b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/password-reset-api/plugin.properties
new file mode 100644
index 0000000000000000000000000000000000000000..18c916fc9a59e026ae19951400e3bf2897d5a9ec
--- /dev/null
+++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/password-reset-api/plugin.properties
@@ -0,0 +1,7 @@
+label = Password Reset API
+class = ch.systemsx.cisd.openbis.dss.generic.server.plugins.jython.JythonIngestionService
+script-path = password-reset-api.py
+password-reset-request-subject = ELN-LIMS password reset for %s
+password-reset-request-body = Hi,\n\nA request has been made to reset the password of user %s.\n\nClick on this link in order to get a new password:\n%s\n\nSincere regards,\nYour ELN-LIMS Team
+new-password-subject = ELN-LIMS new password for %s
+new-password-body = Hi,\n\nYour new password is: %s\n\nPlease login and change it immediately.\n\nSincere regards,\nYour ELN-LIMS Team