loklak_depot – The Beginning: Accounts (Part 3)

So this is my third post in this five part series on loklak_depot’s early beginnings and the Accounts system. Once I am done detailing the Accounts system, we will get into the more advanced usages of loklak_depot wrt data collection and Q&A applications, so it won’t really be a ‘beginning’ anymore.

I’ll be focusing on the Emailing system (for Password Recovery) in this post and talking about how I implemented it. The next two posts will focus on using the Emailing system for SignUp verification, and Password Resetting.

As shown in the previous two posts, I had implemented the login and SignUp page. But there was a functionality not yet implemented: the “Forgot Password” aka Password Recovery, which obviously is important for the entire system to function the way it should. The idea was to lead the user to a page when he clicked on the “Forgot Password?” button on the Sign Up page. This page asks for the user’s email ID, and once this email ID is verified to be in the database, a verification email is sent to it.

Again, as had been done till now, I implemented a servlet to do all this, but along with that, I felt that probably the emailing system could be used elsewhere as well (it turns out it did get used, in the SignUp verification, about which I’ll talk in a later blog post).

So I created a generalised class which can be used to send emails, given some configuration inputs in the config file. I named it the LoklakEmailHandler class.

Let’s check out the configuration inputs. The following snippet is from the config.properties file:



# this is the message server configuration file

# to customize these settings place a file 'customized_config.properties' to
# the path data/settings/

# link to this installation. 
# used for e.g. verification emails
# TODO: should be set during installation
# TODO: should replace shortlink url 
host.url=http://localhost:9000

# allow public signup (false, email, admin, true)
# admin means the admin has to enable the account
# email means a validation email will be send to the user
users.public.signup=false

# Regular expression for passwords (warning: has to be a valid java string, escape characters need to be double escaped e.g. \d instead of d
users.password.regex=^(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{6,64}$
users.password.regex.tooltip=Enter a combination of atleast six characters, containing lower- and uppercase letters and numbers

# SMTP emailing switch (for password recovery and signup verification etc)
smtp.mails.enabled=false

# SMTP password recovery server host (change this as per need)
smtp.host.name=smtp.gmail.com

# sender email (change this as per need)
[email protected]

# depending on security of recovery host server, set encryption as none, starttls or tls
# also make changes on server side: eg if server is gmail, turn POP3 and SMTP on
smtp.host.encryption=tls

# incase encryption = starttls or tls, set sender password and starttls/tls port
smtp.host.senderpass=randomxyz
smtp.host.port=465

For sending emails, since we did not set up a server by ourselves yet, we let the user set it up for themselves. The config fields are thus self-explanatory and are typical specified fields for SMTP servers. Additionally, for specifying password strengths in the Sign Up page (as spoken about before), we now have a config fields where we specify the regex of password and the tooltip message (of the input box) to be shown, so that the user by itself has control over the password security. And to toggle the entire email sending process (be it in SignUp verification or Password Recovery), we have another field, smtp.mails.enabled.

Now comes the fun part, and the heart of the Email Handling process: the LoklakEmailHandler class. The code looks like this:


public class LoklakEmailHandler {

	private static Pattern pattern;
	public static final String EMAIL_PATTERN = "^[_A-Za-z0-9-\+]+(\.[_A-Za-z0-9-]+)*@"
			+ "[A-Za-z0-9-]+(\.[A-Za-z0-9]+)*(\.[A-Za-z]{2,})$";

	public static void sendEmail(String addressTo, String subject, String text) throws Exception {
		
		if (!"true".equals(DAO.getConfig("smtp.mails.enabled", "false"))) {
			throw new ConfigurationException("Mail sending disabled");
		}
		
		pattern = Pattern.compile(EMAIL_PATTERN);
		
		ClientCredential credential = new ClientCredential(ClientCredential.Type.passwd_login, addressTo);
		String sender = DAO.getConfig("smtp.host.senderid", "[email protected]");
		String pass = DAO.getConfig("smtp.host.senderpass", "randomxyz");
		String hostname = DAO.getConfig("smtp.host.name", "smtp.gmail.com");
		String type = DAO.getConfig("smtp.host.encryption", "tls");
		String port = DAO.getConfig("smtp.host.port", "465");

		if (!pattern.matcher(addressTo).matches()) {
			throw new Exception("Invalid email ID");
		}
		if (!pattern.matcher(sender).matches()) {
			throw new Exception("Invalid sender ID");
		}

		if (DAO.authentication.has(credential.toString())
				&& ("none".equals(type) || "tls".equals(type) || "starttls".equals(type)))

		{
			java.util.Properties props;

			if ("none".equals(type)) {
				props = System.getProperties();
			} else {
				props = new Properties();
				props.put("mail.smtp.auth", true);
				props.put("mail.smtp.port", port);
			}

			props.put("mail.smtp.host", hostname);
			props.put("mail.debug", true);

			if ("starttls".equals(type)) {
				props.put("mail.smtp.starttls.enable", true);
			} else if ("tls".equals(type)) {
				props.put("mail.smtp.socketFactory.port", port);
				props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
			}

			Session session;
			if ("none".equals(type)) {
				session = Session.getInstance(props, null);
			} else {
				session = Session.getInstance(props, new javax.mail.Authenticator() {
					protected PasswordAuthentication getPasswordAuthentication() {
						return new PasswordAuthentication(sender, pass);
					}
				});
			}

			try {

				MimeMessage message = new MimeMessage(session);
				message.addHeader("Content-type", "text/HTML; charset=UTF-8");
				message.addHeader("format", "flowed");
				message.addHeader("Content-Transfer-Encoding", "8bit");
				message.setSentDate(new Date());
				message.setFrom(new InternetAddress(sender));
				message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(addressTo, false));
				message.setSubject(subject, "UTF-8");
				message.setText(text, "UTF-8");
				Transport.send(message);
				Log.getLog().debug("status: ok", "reason: ok");

			} catch (MessagingException mex) {
				throw mex;
			}

		} else {
			throw new Exception("Receiver email does not exist or invalid encryption type");
		}
	}
}

The idea is simple. The sendEmail function takes in the Receiver’s ID (subject to regex verification by the EMAIL_PATTERN variable), Subject and Text of email, reads the config values, and depending upon the encryption level (none, starttls or tls) , adds one or more properties to the email properties. Once that is done, it uses the MimeMessage and Transport classes to send the mail. So one just has to specify three fields in total (which are obviously typical of an email) and the email gets sent. Simple, right?

So now that the main part of email sending was covered by LoklakEmailHandler, now comes the relatively small task of using this class in action in the Password Recovery servlet. Here’s how it works: (PasswordRecoveryService.java)


public class PasswordRecoveryService extends AbstractAPIHandler implements APIHandler {

	private static final long serialVersionUID = 3515757746392011162L;

	@Override
	public String getAPIPath() {
		return "/api/recoverpassword.json";
	}

	@Override
	public APIServiceLevel getDefaultServiceLevel() {
		return APIServiceLevel.PUBLIC;
	}

	@Override
	public APIServiceLevel getCustomServiceLevel(Authorization auth) {
		return APIServiceLevel.ADMIN;
	}

	@Override
	public JSONObject serviceImpl(Query call, Authorization rights) throws APIException {
		JSONObject result = new JSONObject();

		String usermail;
		try {
			usermail = URLDecoder.decode(call.get("forgotemail", null), "UTF-8");
		} catch (UnsupportedEncodingException e) {
			result.put("status", "error");
			result.put("reason", "malformed query");
			return result;
		}
		
		String subject = "Password Recovery";
		String body = "Recover password using this link";
		try {
			LoklakEmailHandler.sendEmail(usermail, subject, body);
			result.put("status", "ok");
			result.put("reason", "ok");
		} catch(Exception e){
			result.put("status", "error");
			result.put("reason", e.toString());
		}
		return result;
	}

}

Simple as it is, the servlet receives the forgotemail i.e the email entered for the Password Recovery (which was POSTed from the Password Recovery page), and then tries sending the email to that email address.

As of now, since the actual Password Reset option is under development, we did not specify the reset link (which goes inside the body of the email). For the Password Reset, there were changes to made to the Authentication object too, so I will cover that in a separate blog post.

I’ll be back with the final two posts in this series, both in the coming week, where I’ll be talking about Sign Up verification and the Password Reset system. As always, feedback is duly welcome. 🙂

loklak_depot – The Beginning: Accounts (Part 3)

One thought on “loklak_depot – The Beginning: Accounts (Part 3)

Comments are closed.