loklak_depot – The Beginning: Accounts (Part 5)

So here we are, the culmination of the Accounts system. This final blog post in the series will be on the Password Reset system. It will be a continuation of the Password Recovery system, and takes in concepts from the SignUp Verification system, so if you haven’t read those two yet, I strongly recommend you do so.

From the Password Recovery system, we now know how to accept an email ID from the user (whose email needs to be recovered), and send him an email with a Password Reset link. What we now need to do, is implement the reset link, and make a system which actually resets the password. Again, we’re gonna do this using Servlets.

First up, we needed to figure out the reset link. Here, we’ll do exactly as we did in the SignUp verification: use tokens. We give them a link to the Reset Password page (will talk about it after this) and send in a token so that the link is unique. So in the PasswordRecoveryService servlet, I added the tokens feature as well.

The way I did it is the same as what I wrote in the last article, so I’ll just show the code here, for the explanation please refer to the last article:


String usermail;
		try {
			usermail = URLDecoder.decode(call.get("forgotemail", null), "UTF-8");
		} catch (UnsupportedEncodingException e) {
			throw new APIException(400, "malformed query");
		}

//code redacted on purpose, read below

		String subject = "Password Recovery";
		try {
			LoklakEmailHandler.sendEmail(usermail, subject, getVerificationMailContent(token));
			result.put("message", "Recovery email sent to your email ID. Please check");
		} catch (Exception e) {
			result.put("message", e.toString());
		}
		return result;
	}


	private String getVerificationMailContent(String token) {

		String verificationLink = DAO.getConfig("host.name", "http://localhost:9000")
				+ "/apps/resetpass/index.html?token=" + token;
		String result;
		try {
			result = IO.readFileCached(Paths.get(DAO.conf_dir + "/templates/reset-mail.txt"));
		} catch (IOException e) {
			result = "";
		}

		result = result.contains(resetLinkPlaceholder) ? result.replace(resetLinkPlaceholder, verificationLink)
				: verificationLink;

		return result;
	}

We basically get the forgotemail (i.e email ID whose password user forgot which he entered in the Password Recovery page). We check if the email even exists in the Authentication database: if it does, the recovery email is sent to him. The token generation is obvious (resetpass is the Password Reset page as I mentioned), and like in the SignUp verification, we again kept a file reset-mail.txt which is the body of the email we have to send. But, there’s another portion in that code which I redacted. What did I do in there?

Now, the user gets the mail, and he clicks the link. He is led to localhost:9000/apps/resetpass/index.html?token=<random-token> (localhost in this case). What we want now is the program to figure out from the token what email ID’s password needs to be reset, and then once the user resets the password from that Email ID, make the changes to the database. Now comes the main part i.e resetauth

I had to figure out what email ID the token refers to, so I figured out, the best solution would be to have another JsonTray in DAO, which maps token names with email IDs and also contains their expiry times. The reason why I did so, and didn’t do the other way round, will be explained later in this article.

So, I decided to add another enum to ClientCredential.Type, named resetpass_token which stores the keys (i.e tokens) for the JsonTray (which I named passwordreset and it accesses passwordreset.json). And I made the JsonTray non-persistent, i.e it has a definite size.


Path passwordreset_path = settings_dir.resolve("passwordreset.json");
passwordreset = new JsonTray(passwordreset_path.toFile(), 10000);
OS.protectPath(passwordreset_path);

And ClientCredential.Type looks like:


public enum Type {
    	passwd_login(true),
        cookie(false),
        access_token(false),
        resetpass_token(false),
        host(false);
        private final boolean persistent;
        Type(final boolean persistent) {
            this.persistent = persistent;
        }
        public boolean isPersistent() {
            return this.persistent;
        }
    }

So here’s what we do once we get forgotemail (i.e the Email ID whose password is forgotten): We create a random token, add it as a ClientCredential.Type.access_token key to passwordreset.json, and then to the JSONObject we add its expiry time, and the ID etc. Basically, resetauth is an Authentication object which points to the JSONObject with the appropriate token. So, this is the code to be added in place of //code redacted as written above:


ClientCredential credential = new ClientCredential(ClientCredential.Type.passwd_login, usermail);
		ClientIdentity identity = new ClientIdentity(ClientIdentity.Type.email, credential.getName());

		if (!DAO.authentication.has(credential.toString())) {
			throw new APIException(422, "email does not exist");
		}

		String token = createRandomString(30);
		ClientCredential tokenkey = new ClientCredential(ClientCredential.Type.resetpass_token, token);
		Authentication resetauth = new Authentication(tokenkey, DAO.passwordreset);
		resetauth.setIdentity(identity);
		resetauth.setExpireTime(7 * 24 * 60 * 60);
		resetauth.put("one_time", true);

This completes most of PasswordRecoveryService, except one part, which I’ll now come to.

So now, when the email is sent, we store the token and the corresponding email ID in a non-persistent database. And now, the user clicks on the link in the email. The link has a token and it leads to the Password Reset page. We now need to figure out whose email ID’s password needs to be reset, given the token and the database, and then display that email ID to the user (so that he knows he’s resetting the right email ID). So this is the last part of the code of PasswordRecoveryService:

In the Password Reset page (resetpass), I made an AJAX GET call to the PasswordRecoveryService servlet, and sent them two parameters: getParameters = true and the token of the url. This is how the AJAX call looks like:


function getParameter(parameter) {  //parses URL and returns the value of the GET parameter

    	var params = window.location.search.substr(1).split('&');
    	
    	for (var i = 0; i < params.length; i++) {
    		var p=params[i].split('=');
    		if (p[0] == parameter) {
    			return p[1];
    		}   
    	}
    	return null;
    }

var urltoken = getParameter('token'); //retrieves value of token from the url

$.ajax(	"/api/recoverpassword.json", {
		data: { getParameters: true, token: urltoken },
		dataType: 'json',
		success: function (response) {
			regex = response.regex;
			var regexTooltip = response.regexTooltip;
			$('#pass').tooltip({'trigger':'focus', 'placement': 'left', 'title': regexTooltip});
			$('#status-box').text(response.message);
			tokenerr = false;
		},
		error: function (xhr, ajaxOptions, thrownError) {
			$('#status-box').text(thrownError);
			$('#status-box').addClass("error");
			$('#pass').prop( "disabled", true );
			$('#confirmpass').prop( "disabled", true );
			$('#resetbut').prop( "disabled", true );
			tokenerr = true;
		},
	});

And the last part of PasswordRecoveryService, i.e the part which processes this AJAX call, is this:


JSONObject result = new JSONObject();

		// check if token exists

		if (call.get("getParameters", false)) {
			if (call.get("token", null) != null && !call.get("token", null).isEmpty()) {
				ClientCredential credentialcheck = new ClientCredential(ClientCredential.Type.resetpass_token,
						call.get("token", null));
				if (DAO.passwordreset.has(credentialcheck.toString())) {
					Authentication authentication = new Authentication(credentialcheck, DAO.passwordreset);
					if (authentication.checkExpireTime()) {
						String passwordPattern = DAO.getConfig("users.password.regex", "^(?=.*\d).{6,64}$");
						String passwordPatternTooltip = DAO.getConfig("users.password.regex.tooltip",
								"Enter a combination of atleast six characters");
						result.put("message", "Email ID: " + authentication.getIdentity().getName());
						result.put("regex", passwordPattern);
						result.put("regexTooltip", passwordPatternTooltip);
						return result;
					}
					authentication.delete();
					throw new APIException(422, "Expired token");
				}
				throw new APIException(422, "Invalid token");
			} else {
				throw new APIException(422, "No token specified");
			}
		}

This code is pretty obvious: we retrieve the token, and if it exists in the passwordreset.json AND the current time is before the expiry date of the token (we get the expiry time using Instant.now.getEpochSecond(), I’m not going into the entire code of the expiry time because it’s a bit long), we retrieve the token’s email ID and display it to the user. Else, it gives an error (Expired Token, Invalid Token or No token as per the condition).

So now at the resetpass page (it’s a simple HTML page so I’ll focus on the JavaScript rather than the page design etc), the user can see his email ID on the screen, he sets a new password, and clicks on Submit. On clicking of the button, we need to reset the password in the actual database, which is Authentication (i.e DAO.authentication).

So the final part of the entire Password Recovery / Reset system comes here, the way to actually reset the password. For this, we create another servlet, the PasswordResetServlet. It accesses the DB, resets the password if it can, and displays the appropriate message to the user. This is the code of PasswordResetServlet: (API endpoint set as /api/resetpassword.json)


JSONObject result = new JSONObject();

		String newpass;
		try {
			newpass = URLDecoder.decode(call.get("newpass", null), "UTF-8");
		} catch (UnsupportedEncodingException e) {
			throw new APIException(400, "malformed query");
		}

		ClientCredential credential = new ClientCredential(ClientCredential.Type.resetpass_token,
				call.get("token", null));
		Authentication authentication = new Authentication(credential, DAO.passwordreset);
		ClientCredential emailcred = new ClientCredential(ClientCredential.Type.passwd_login,
				authentication.getIdentity().getName());

		String passwordPattern = DAO.getConfig("users.password.regex", "^(?=.*\d).{6,64}$");

		Pattern pattern = Pattern.compile(passwordPattern);

		if ((authentication.getIdentity().getName()).equals(newpass) || !pattern.matcher(newpass).matches()) {
			// password can't equal email and regex should match
			throw new APIException(400, "invalid password");
		}

		if (DAO.authentication.has(emailcred.toString())) {
			Authentication emailauth = new Authentication(emailcred, DAO.authentication);
			String salt = createRandomString(20);
			emailauth.remove("salt");
			emailauth.remove("passwordHash");
			emailauth.put("salt", salt);
			emailauth.put("passwordHash", getHash(newpass, salt));
		}

		if (authentication.has("one_time") && authentication.getBoolean("one_time")) {
			authentication.delete();
		}
		result.put("message", "Your password has been changed!");
		return result;

On clicking of the submit button, we send the newly set password (in encoded URI format) AND the token from the URL (remember, we had done the token processing earlier in the getParameters ONLY to show the user the email ID he’s resetting, we hadn’t actually done the actual resetting so we send the token back to the PasswordResetServlet). We retrieve both the new password and the token. Then straightaway, we look out for the token in the passwordreset.json, get the email ID from there, then go to Authentication.json. We remove the salt and the passwordHash from there, create a new salt, and store another passwordHash by encoding the newpass (i.e the new password) with the salt. Once this is done, we then go back to passwordreset.json and remove the token from there, since it was one-time, so the token can’t be used again. This finishes the process, and now, the user’s password is reset.

Phew, that was long!

So finally we have seen how the accounts system of loklak_depot was developed. This was just the mere beginning of what’s next to come and was a relatively trivial part (which was only complicated because it was all servlet based and we developed all the security measures ourselves). The next thing will be when loklak_depot’s functionality actually begins: Q&A apps, apps which use the Twitter data and give the user valuable information by using the APIs available. This is what I’ll be working on now, and will start talking about it in my next article. Feedback, as always, is welcome. 🙂

loklak_depot – The Beginning: Accounts (Part 5)

Under the hood: Authorization

This post is about the current status of the authorization system and will focus on the parts that are nessecary to understand to write API-servlets.

Permissions

In order to understand the following concept, we first have to clearify how we manage permissions. A permission by itself is a key-value pair, declared on servlet-level. Think something like this:

allowed-to-download : true

max-downloads : 10

We currently have three levels on which permissions get set:

Base-User-Roles

We have a small set of hardcoded roles, we call them base-user-roles, which get permissions assigned directly in the java code.

These permissions can be seen as default permissions. They can be overritten for user-roles or individual users.

We currently have the following base-user-roles:

ANONYMOUS, // the lowest user-roles, usually for not logged-in users
USER, // normal user-roles
PRIVILEGED, // user-roles with special privileges like moderators
ADMIN // admin-roles

User-Roles

Not hardcoded but fully configurable, we have user-roles. Each user-role has a parent from which it inherits permissions. The parent can either be a base-user-role or another user-role. In the end, there must always be a base-user-role at the root of the tree.

Each user-role can override permissions inherited from it’s parent, making them strongly configurable.

By default, the user-roles are directly derived from the base-user-roles and do not have any overrides.

Users

At the end, there’re the individual users. Each user has a user-role, from which she inherits permissions. But of course, she can again have individual overrides.

 

Servlets

In order to use the AAA-system (Authentication, Authorization, Accounting), a servlet has to extend the AbstractAPIHandler class. It therefor has to override four methods:

public BaseUserRole getMinimalBaseUserRole()
public JSONObject getDefaultPermissions(BaseUserRole baseUserRole)
public String getAPIPath()
public JSONObject serviceImpl(Query post, 
Authorization rights, final JSONObjectWithDefault permissions)

The third  is just about the path of the servlet. The fourths is the actual implementation of the servlet, we will come back to that shortly.

The first two are exclusevly for the authorization system. Here’s some example implementation:

@Override
public BaseUserRole getMinimalBaseUserRole() {
   return BaseUserRole.PRIVILEGED;
}

This means, that the user-role of the user accessing the servlet must be derived atleast from the PRIVILEGED-base-user-role. Of course, ADMIN would also be ok. All other users get a 401-HTTP-Error. This method is intended to make it very explicit and hard to confuse if a servlet should be limited to, for example, admins only. For more sophisticated permission management, we use the second method:

@Override
public JSONObject getDefaultPermissions(BaseUserRole baseUserRole){
   JSONObject result = new JSONObject();

   switch(baseUserRole){
      case ADMIN:
         result.put("list_users", true);
         result.put("list_users-roles", true);
         result.put("edit-all", true);
         result.put("edit-less-privileged", true);
         break;
      case PRIVILEGED:
         result.put("list_users", true);
         result.put("list_users-roles", true);
         result.put("edit-all", false);
         result.put("edit-less-privileged", true);
         break;
      default:
         result.put("list_users", false);
         result.put("list_users-roles", false);
         result.put("edit-all", false);
         result.put("edit-less-privileged", false);
         break;
   }
   return result;
}

Here we define default permissions based on the base-user-role. The example is from the user-management servlet, so we only allow administrators and special priviledged users anyway. The default values would not be used in this example.

In this method we should declare all keys we want to use in the servlet. By default, we allow privileged users to edit the profiles of users with the base-user-role USER or ANONYMOUS.

We maybe want a user-role that is able to list the users, but is not able to edit any user. We could archive that by overriding ‘edit-less-privileged’ to ‘false’. This will be possible via a the user-management servlet, for which we’ll have a graphical app.

So how do we use it? Here’s is how the ‘serviceImpl` could look like:

@Override
public JSONObject serviceImpl(Query post, Authorization authorization,
final JSONObjectWithDefault permissions) throws APIException {

   JSONObject result = new JSONObject();

   switch (post.get("show","")){
      case "user-list":
         if(permissions.getBoolean("list_users", false)){
            result.put("user-list", DAO.authorization.getPersistent());
         } else throw new APIException(403, "Forbidden");
         break;
      default: throw new APIException(400, "No 'show' parameter specified");
   }

   return result;
}

The ‘permission’ object contains all the values for this servlet, from the base-user-roles, the overrides from the user-role(s) and the overrides for the specific user.

It’s actually a ‘JSONObject’, but given as ‘JSONObjectWithDefault’, which just extends the ‘get’ methods of ‘JSONObject’ with default values. This is just to avoid security issues because of errors.

We can also get the permissions for this or other servlets from the Authorization servlet, by calling ‘authorization.getPermissions(this);’ or ‘authorization.getPermissions(new SignUpService());’ We actually have to initialize a object for it, as Java does not support methods that are static and abstract at the same time.

I hope that gives you an idea how to currently use authorization in servlets 🙂

Note: from here on, Michael will take over the further development of the AAA-system, so some things might change in the future.

Under the hood: Authorization

loklak_depot – The Beginning: Accounts (Part 4)

So here I am with another blog post. This, my fourth post in this series, will focus on SignUp verification and how I implemented it. The fifth and the final post in this series, will be on Password Resetting. This will finish the Accounts system, and I’ll then move onto talking about some Twitter analysis applications I’m working on (the Q&A apps I spoke about in the previous posts).

I had covered the Sign Up page before, but there was still a problem. What if the page were to be spammed by bots / unauthorised users? It would then create unnecessary number of accounts and DAO.authentication will then overflow. That’s why it was deemed necessary to have a SignUp verification system.

The SignUp verification system is simple: send email to user once he signs up. The email consists of a verification link which has a token which can be used to verify. Clicking on that will verify his account.

Without further ado, let’s dive into the code. The following code snippet should be self explanatory through the added comments, though I’ll explain at the bottom:


		if (post.get("signup", null) == null || post.get("password", null) == null) {
			throw new APIException(400, "signup or password empty");
		}

		// get credentials
		String signup, password;
		try {
			signup = URLDecoder.decode(post.get("signup", null), "UTF-8");
			password = URLDecoder.decode(post.get("password", null), "UTF-8");
		} catch (UnsupportedEncodingException e) {
			throw new APIException(400, "malformed query");
		}

		// check email pattern
		Pattern pattern = Pattern.compile(LoklakEmailHandler.EMAIL_PATTERN);
		if (!pattern.matcher(signup).matches()) {
			throw new APIException(400, "no valid email address");
		}

		// check password pattern
		String passwordPattern = DAO.getConfig("users.password.regex", "^(?=.*\d).{6,64}$");

		pattern = Pattern.compile(passwordPattern);

		if (signup.equals(password) || !pattern.matcher(password).matches()) {
			throw new APIException(400, "invalid password");
		}

		// check if id exists already

		ClientCredential credential = new ClientCredential(ClientCredential.Type.passwd_login, signup);
		Authentication authentication = new Authentication(credential, DAO.authentication);

		if (authentication.getIdentity() != null) {
			throw new APIException(422, "email already taken");
		}

		// create new id
		ClientIdentity identity = new ClientIdentity(ClientIdentity.Type.email, credential.getName());
		authentication.setIdentity(identity);

		// set authentication details
		String salt = createRandomString(20);
		authentication.put("salt", salt);
		authentication.put("passwordHash", getHash(password, salt));
		authentication.put("activated", activated);

		// set authorization details
		Authorization authorization = new Authorization(identity, DAO.authorization, DAO.userRoles);
		authorization.setUserRole(DAO.userRoles.getDefaultUserRole(BaseUserRole.USER));

		if (sendEmail) {
			String token = createRandomString(30);
			ClientCredential access_token = new ClientCredential(ClientCredential.Type.access_token, token);
			Authentication tokenAuthentication = new Authentication(access_token, DAO.authentication);
			tokenAuthentication.setIdentity(identity);
			tokenAuthentication.setExpireTime(7 * 24 * 60 * 60);
			tokenAuthentication.put("one_time", true);

			try {
				LoklakEmailHandler.sendEmail(signup, "Loklak verification", getVerificationMailContent(token));

				result.put("message",
						"You successfully signed-up! An email with a verification link was send to your address.");

			} catch (ConfigurationException e) {
				result.put("message",
						"You successfully signed-up, but no email was sent as it's disabled by the server.");
			} catch (Exception e) {
				result.put("message",
						"You successfully signed-up, but an error occurred while sending the verification mail.");
			}
		} else {
			result.put("message", "You successfully signed-up!");
		}

		return result;
	}

This code is from a class SignUpService.java which again extends AbstractAPIHandler (please see my previous blogposts and Robert’s posts if you aren’t clear about it), so post is obviously the POST request, and is of type Query, and this code returns a JSON (as AbstractAPIHandler implements an abstract method called serviceImpl which returns JSON). We use APIException (a predefined exception class we made) to make it more convenient to report errors with codes (earlier we used JSON). The signup and password posted from the webpage are encoded URI components, so we decode them. The rule for password and email is that they should match the regex scheming + they can’t be equal to each other and that email should be something not in the DB i.e DAO.authentication. The latter is checked by simply using the ClientCredential keys (passwd_login enum) to access DAO.auth and checking.

Here comes the verification part. We first encode the password in SHA256 using a random salt. If the user is permitted to send the email (I’ll come to that in just a bit), we set an access token for it (random int), give it an expiry time, keep it one-time, sent the token as a GET parameter to the base URL i.e localhost (in this case), and mail the url to him using loklakemailhandler.

This is how the getVerificationMailContent function looks like. It makes up the URL and fixes the email body.


private String getVerificationMailContent(String token){
    	
    	String verificationLink = DAO.getConfig("host.name", "http://localhost:9000") + "/api/signup.json?access_token="+token+"&validateEmail=true&request_session=true";
    	
    	// get template file
    	String result;
    	try{
    		result = IO.readFileCached(Paths.get(DAO.conf_dir + "/templates/verification-mail.txt"));
    	} catch(IOException e){
    		result = "";
    	}
    	
    	result = result.contains(verificationLinkPlaceholder) ? result.replace(verificationLinkPlaceholder, verificationLink) : verificationLink;
    	
    	return result;
    }

where the value of verificationLinkPlaceholder is "%VERIFICATION-LINK%". We take up the token, and send it as a GET parameter along with two more params (validateEmail=true and request_session=true) to the signup servlet. The body of the email contains this URL, which we are reading from a custom file which has the placeholder. The file is as follows: (verification-mail.txt)


You just signed up for Loklak!

To finish the process, you have to verify you email address by clicking on the following link:

%VERIFICATION-LINK%

If you didn't sign up for Loklak, please ignore this email.

As is self explanatory, the link is inserted in the place of %VERIFICATION-LINK%. We read the file using IO.readFileCached and replace using replace as is seen.

Now the last part. What happens when the user clicks on the link? It still leads to the SignUp servlet, isn’t it? So what happens? The following code snippet answers that very question:


public JSONObject serviceImpl(Query post, Authorization rights, final JSONObjectWithDefault permissions) throws APIException {

    	BaseUserRole serviceLevel = getCustomServiceLevel(rights);
    	
    	JSONObject result = new JSONObject();
    	
    	// if regex is requested
    	if(post.get("getParameters", false)){
    		String passwordPattern = DAO.getConfig("users.password.regex", "^(?=.*\d).{6,64}$");
    		String passwordPatternTooltip = DAO.getConfig("users.password.regex.tooltip", "Enter a combination of atleast six characters");
    		if("false".equals(DAO.getConfig("users.public.signup", "false"))){
				throw new APIException(403, "Public signup disabled");
    		}
    		result.put("regex", passwordPattern);
    		result.put("regexTooltip", passwordPatternTooltip);
    		
    		return result;
    	}
    	
    	// is this a verification?
    	if(post.get("validateEmail", false) && serviceLevel.ordinal() > BaseUserRole.ANONYMOUS.ordinal()){
    		ClientCredential credential = new ClientCredential(ClientCredential.Type.passwd_login, rights.getIdentity().getName());
    		Authentication authentication = new Authentication(credential, DAO.authentication);
    		
    		authentication.put("activated", true);

    		result.put("message", "You successfully verified your account!");
    		return result;
    	}
    	
    	
    	
    	// check if this is done by admin or public and if verification is needed
    	boolean activated = true;
    	boolean sendEmail = false;
    	
    	if(serviceLevel != BaseUserRole.ADMIN){
    		switch(DAO.getConfig("users.public.signup", "false")){
    			case "false":
                    throw new APIException(403, "Public signup disabled");
    			case "admin":
    				activated = false;
    				break;
    			case "email":
    				activated = false;
    				sendEmail = true;
    		}
    	}

Just a reminder: all the code before this one, was also a part of serviceImpl function, so the function gets completed now.

We GET the validateEmail variable from the POST request (if the access level of the user is above anonymous i.e he / she is authorised), and if it is true, we simply convert the access_token key (which was used to store the email ID) into passwd_login, so that it is like all the other email IDs (all verified email IDs have key passwd_login, and access_token key is for those for which we need to verify). Also, as in the last article, we had four options for users.public.signup: false, admin, email and true. The default (i.e true) is that the account is activated and no email will be sent (i.e SignUp page will directly lead to activation). For admin (since admin is for testing purposes), the account won’t really be activated but he can check if the system works. For sending the verification email, we set the config property to “email”, making sendEmail (spoken about earlier) true for “email”. That’s all there is.

And just one last question you may be having: what is the regex code for (written above the validation code)? We made a small improvement to the SignUp system: instead of hardcoding password strength by ourselves, we let the admin to decide for himself, so we kept password strength and password strength description as fields in the config file. And we modified the JavaScript of SignUp page, we added a JQuery AJAX call as follows:


$.ajax(	"/api/signup.json", {
			data: { getParameters: true },
			dataType: 'json',
			success: function (response) {
                regex = response.regex;
                var regexTooltip = response.regexTooltip;
                $('#pass').tooltip({'trigger':'focus', 'placement': 'left', 'title': regexTooltip});
			},
			error: function (xhr, ajaxOptions, thrownError) {
			    $('#status-box').text(thrownError);
                $('#status-box').addClass("error");
                $('#email').prop( "disabled", true );
                $('#pass').prop( "disabled", true );
                $('#confirmpass').prop( "disabled", true );
                $('#signup').prop( "disabled", true );
			},
	});

We simply send an AJAX call with the getParameters variable to the SignUp servlet (which again retrieves this variable and returns a JSON which specifies the regex descriptions). We use this JSON and retrieve the regex and the regex descriptions from it, and display that on the webpage (we modified the Password Strength function accordingly as well) Additionally, we also kept a status-box to display errors (if there is any error i.e Public SignUp disabled / emails disabled) which will display the error on the screen itself and block all the fields of the SignUp form.

So that’s it for the SignUp verification. My next and final article on the Accounts system will be on the Password Reset system and how we finally got the Password Recovery system to a full circle. Feedback, as always, is welcome. 🙂

loklak_depot – The Beginning: Accounts (Part 4)

Coveralls and Travis support for Loklak APIs

Today I will be talking about Coveralls and Travis integration into Loklak APIs.

Travis CI

Wikipedia defines it as:

Travis CI is a hosted, distributed continuous integration service used to build and test software projects hosted at GitHub.

Travis takes care of the tests and deployment of apps. Travis integration is really easy when you have your tests ready. It was a basic three step process:

  1. Registering the API repos with Travis.
    Selection_047
  2. Adding .travis.yml to the API github repository. e.g. For loklak_php_api , the file looks like this.
    Selection_048
    language and php versions are specified for which the API is to be tested.  We added phpunit Tests in script since we wanted Travis to execute the tests.
  3. Trigger the first build with a git push

And the first build is up.

Selection_049

Coveralls

Coveralls is a presentation of the coverage of test suite you pass to Travis to build. In layman language, Coveralls helps you find the lines in your code which were not tested by your Test Suite.

Coveralls integration is also simple, but might involve some googling if you are working on PHP. I will take the example of loklak_php_api here.

  1. Registering the API repo with Coveralls.
    Selection_051
  2. Modifying the .travis.yml to this
    Selection_052
  3. Creating a .coveralls.yml file to specify the paths required to send the coverage to Coveralls
    Selection_053
  4. Since testing of only loklak.php was required, so a phpunit.xml file was added to exclude Tests and Requests directories from test suite.
    Selection_054

After configuring the environment, there we have it. Coveralls result showing the code coverage of our test suite on loklak.php!
Selection_050

Coveralls and Travis support for Loklak APIs

Loklak Walls Manual Moderation – approving tweets

This is a continuation from the previous post – Loklak walls manual moderation – tweet storage. In this section, I will show the changes I made to enable the user to approve or reject a tweet, and make that change happen across all walls that are opened.

First, I had to examine how loklak.net displays it’s tweets previously. On each wall page, a timeout would be called every interval to retrieve new tweets from loklak_server, these tweets would then be stored on the client’s browser window, in the view model, or $scope in angular.

[code language=”javascript”]

vm.update2 = function(refreshTime) {
return $timeout(function() {
SearchService.initData(searchParams).then(function(data) {

vm.statuses = data.statuses.splice(0, searchParams.count);

}, refreshTime);
};
[/code]

 

Having this kind of storage for each browser makes the data inconsistent across the same wall open in different browser windows, as they start querying loklak_server at different times and intervals. This also makes it tough to implement manual moderation previously as the tweets on each open page could not be controlled from the dashboard, since they all lived in their own page.

Now that we have shifted the storage of tweets to mongoDB, we are now able to control how tweets are displayed for all open walls! First, I shifted the calls to loklak_server to the dashboard page instead of the walls page as it made more sense to control the interval from the dashboard rather than the display pages themselves.

Next, I needed a way to sync changes in the database across the dashboard as well as the display pages. At first, I tried to use the same method of http calls, but I soon found them too complicated to sync, having 3 components with interconnected actions. Actions from the dashboard and new entries from the database would have to affect the display, and new entries from the database would have to affect the dashboard and display. Also having an interval for updating the wall after changes were made to the database made it seem very unresponsive and resulted in a bad user experience.

The solution to this was: WebSockets! This allows us to listen for new events like addition of new tweets. When first initialized, the display pages and the dashboard just had to load the existing tweets in the database, when new tweets are loaded, they’ll be added into the database AND the displays and dashboard, making it update in real time.

websocket-small

I chose socket.io as it made integrating WebSockets into the MEAN stack relatively easy. After the http request for new tweets from loklak_server is returned, the app then sends a POST request to the node server, which then emits an event to update the display and the dashboard. Below is the route controller, which posts the tweet array received from loklak_server.

[code language=”javascript”]
module.exports.storeTweet = function (req, res) {
req.body.tweetArr.forEach(function(tweet){
var newTweet = new Tweet(tweet);
newTweet.save(function(err,tweet){
// EMIT DASHBOARD EVENT
io.emit("addNewTweet", tweet);
// EMIT WALL DISPLAY EVENT
io.emit("addNewTweet"+req.body.userWallId, tweet);
}
})
});

[/code]

On the wall display page controller, it listens for the emitted event and adds the data to the display.

[code language=”javascript”]
socket.on(‘addNewTweets’ + $stateParams.user + $stateParams.id, function(tweet){
vm.statuses.splice(0,0, tweet);
})
[/code]

The toggle events are similar in that instead of POST requests,  now we are sending PUT requests from the dashboard to update the tweet in mongoDB, and then changing the data attribute on the wall display. Using AngularJS’s ng-hide we can show/hide the tweet depending on it’s approval field.

Inside the angular directive on the dashboard we attach a toggle function to the click:

[code language=”javascript”]
$scope.toggle = function(){
$scope.data.approval = !$scope.data.approval;
$http.put(‘/api/tweets/’+$scope.data._id, $scope.data);
}
[/code]

[code language=”html”]
<div ng-show="data.approval" ng-attr-id="{{data.id_str}}" class="linear linear-simple" /&amp;gt;
[/code]

Similarly on the server we emit an event:

[code language=”javascript”]

module.exports.updateTweet = function (req, res) {
Tweet
.findById(req.params.tweetId)
.exec(function(err, tweet) {
tweet.approval = !tweet.approval;
tweet.save(function(err) {
res.json({tweet: tweet});
});
});

// EMIT TOGGLE EVENT
io.emit("toggle", req.params.tweetId);
}
}
[/code]

And on the wallDisplay controller we can listen to that toggle event:

[code language=”javascript”]

socket.on(‘toggle’,function(tweetId){
var tweetIdx = vm.statuses.findIndex(function(tweet){
return tweet._id === tweetId;
});
vm.statuses[tweetIdx].approval = !vm.statuses[tweetIdx].approval;
});

[/code]

The end result is manual moderation from the dashboard!

icLfu4KZE9

 

 

Loklak Walls Manual Moderation – approving tweets

Convert web pages into structured data

Loklak provides a new API which converts web pages into structured data in JSON. The genericscraper API helps you to scrape any web page from a given URL and provides you with the structured JSON data. Just place the URL in the given format http://localhost:9000/api/genericscraper.json?url=http://www.google.com

This scrapes generic data from a given web page URL, for instance this is the current URL after scraping the main Google search page.

{
  "Text in Links": [
    "Images",
    "Maps",
    "Play",
    "YouTube",
    "News",
    "Gmail",
    "Drive",
    "More »",
    "Web History",
    "Settings",
    "Sign in",
    "Advanced search",
    "Language tools",
    "हिन्दी",
    "বাংলা",
    "తెలుగు",
    "मराठी",
    "தமிழ்",
    "ગુજરાતી",
    "ಕನ್ನಡ",
    "മലയാളം",
    "ਪੰਜਾਬੀ",
    "Advertising Programs",
    "Business Solutions",
    "+Google",
    "About Google",
    "Google.com",
    "Privacy",
    "Terms"
  ],
  "Image files": [],
  "source files": [],
  "Links": [
    "http://www.google.co.in/imghp?hl=en&tab=wi",
    "http://maps.google.co.in/maps?hl=en&tab=wl",
    "https://play.google.com/?hl=en&tab=w8",
    "http://www.youtube.com/?gl=IN&tab=w1",
    "http://news.google.co.in/nwshp?hl=en&tab=wn",
    "https://mail.google.com/mail/?tab=wm",
    "https://drive.google.com/?tab=wo",
    "https://www.google.co.in/intl/en/options/",
    "http://www.google.co.in/history/optout?hl=en",
    "/preferences?hl=en",
    "https://accounts.google.com/ServiceLogin?hl=en&passive=true&continue=http://www.google.co.in/%3Fgfe_rd%3Dcr%26ei%3DR_xpV6G9M-PA8gfis7rIDA",
    "/advanced_search?hl=en-IN&authuser=0",
    "/language_tools?hl=en-IN&authuser=0",
    "http://www.google.co.in/setprefs?sig=0_VODpnfQFFvCo-TLhn2_Kr9sRC2c%3D&hl=hi&source=homepage",
    "http://www.google.co.in/setprefs?sig=0_VODpnfQFFvCo-TLhn2_Kr9sRC2c%3D&hl=bn&source=homepage",
    "http://www.google.co.in/setprefs?sig=0_VODpnfQFFvCo-TLhn2_Kr9sRC2c%3D&hl=te&source=homepage",
    "http://www.google.co.in/setprefs?sig=0_VODpnfQFFvCo-TLhn2_Kr9sRC2c%3D&hl=mr&source=homepage",
    "http://www.google.co.in/setprefs?sig=0_VODpnfQFFvCo-TLhn2_Kr9sRC2c%3D&hl=ta&source=homepage",
    "http://www.google.co.in/setprefs?sig=0_VODpnfQFFvCo-TLhn2_Kr9sRC2c%3D&hl=gu&source=homepage",
    "http://www.google.co.in/setprefs?sig=0_VODpnfQFFvCo-TLhn2_Kr9sRC2c%3D&hl=kn&source=homepage",
    "http://www.google.co.in/setprefs?sig=0_VODpnfQFFvCo-TLhn2_Kr9sRC2c%3D&hl=ml&source=homepage",
    "http://www.google.co.in/setprefs?sig=0_VODpnfQFFvCo-TLhn2_Kr9sRC2c%3D&hl=pa&source=homepage",
    "/intl/en/ads/",
    "http://www.google.co.in/services/",
    "https://plus.google.com/104205742743787718296",
    "/intl/en/about.html",
    "http://www.google.co.in/setprefdomain?prefdom=US&sig=__SF4cV2qKAyiHu9OKv2V_rNxesko%3D",
    "/intl/en/policies/privacy/",
    "/intl/en/policies/terms/",
    "/images/branding/product/ico/googleg_lodp.ico"
  ],
  "language": "en-IN",
  "title": "Google",
  "Script Files": []
}

I wrote a generic scraper using the popular HTML scraper Java library JSoup. I scraped the generic fields like title, images, links, source files and other text between links. After the generic scraper was ready I registered the API endpoint as api/genericscraper.json along with the servlet.

Selection_128

I loaded the page from the given URL and took the value from the variable url. I scraped every tag by using getElementByTag. After storing the elements, I looped through the list and retrieved the attributes from each tag. I stored the data accordingly and pushed into the JSONArray. After the necessary scraping I pushed the JSON Arrays into a JSON object and pretty printed it.

Selection_129

There is an app consuming the above API called WebScraper under the Loklak apps page.

Selection_122.png

Convert web pages into structured data

Now get wordpress blog updates with Loklak !

Loklak shall soon be spoiling its users !

Next, it will be bringing in tiny tweet-like cards showing the blog-posts (title, publishing date, author and content) from the given WordPress Blog URL.

This feature is certain to expand the realm of Loklak’s missive of building a comprehensive and an extensive social network dispensing useful information.

Screenshot from 2016-06-22 04:48:28

In order to implement this feature, I have again made the use of JSoup: The Java HTML parser library as it provides a very convenient API for extracting and manipulating data, scrape and parse HTML from a URL.

The information is scraped making use of JSoup after the corresponding URL in the format "https://[username].wordpress.com/" is passed as an argument to the function scrapeWordpress(String blogURL){..} which returns a JSONObject as the result.

A look at the code snippet :

/**
 *  WordPress Blog Scraper
 *  By Jigyasa Grover, @jig08
 **/

package org.loklak.harvester;

import java.io.IOException;

import org.json.JSONArray;
import org.json.JSONObject;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

public class WordPressBlogScraper {
	public static void main(String args[]){
		
		String blogURL = "https://loklaknet.wordpress.com/";
		scrapeWordpress(blogURL);		
	}
	
	public static JSONObject scrapeWordpress(String blogURL) {
		
                Document blogHTML = null;
		
		Elements articles = null;
		Elements articleList_title = null;
		Elements articleList_content = null;
		Elements articleList_dateTime = null;
		Elements articleList_author = null;

		String[][] blogPosts = new String[100][4];
		
		//blogPosts[][0] = Blog Title
		//blogPosts[][1] = Posted On
		//blogPosts[][2] = Author
		//blogPosts[][3] = Blog Content
		
		Integer numberOfBlogs = 0;
		Integer iterator = 0;
		
		try{			
			blogHTML = Jsoup.connect(blogURL).get();
		}catch (IOException e) {
            e.printStackTrace();
        }
			
			articles = blogHTML.getElementsByTag("article");
			
			iterator = 0;
			for(Element article : articles){
				
				articleList_title = article.getElementsByClass("entry-title");				
				for(Element blogs : articleList_title){
					blogPosts[iterator][0] = blogs.text().toString();
				}
				
				articleList_dateTime = article.getElementsByClass("posted-on");				
				for(Element blogs : articleList_dateTime){
					blogPosts[iterator][1] = blogs.text().toString();
				}
				
				articleList_author = article.getElementsByClass("byline");				
				for(Element blogs : articleList_author){
					blogPosts[iterator][2] = blogs.text().toString();
				}
				
				articleList_content = article.getElementsByClass("entry-content");				
				for(Element blogs : articleList_content){
					blogPosts[iterator][3] = blogs.text().toString();
				}
				
				iterator++;
				
			}
			
			numberOfBlogs = iterator;
			
			JSONArray blog = new JSONArray();
			
			for(int k = 0; k<numberOfBlogs; k++){
				JSONObject blogpost = new JSONObject();
				blogpost.put("blog_url", blogURL);
				blogpost.put("title", blogPosts[k][0]);
				blogpost.put("posted_on", blogPosts[k][1]);
				blogpost.put("author", blogPosts[k][2]);
				blogpost.put("content", blogPosts[k][3]);
				blog.put(blogpost);
			}			
			
			JSONObject final_blog_info = new JSONObject();
			
			final_blog_info.put("Wordpress blog: " + blogURL, blog);			

			System.out.println(final_blog_info);
			
			return final_blog_info;
		
	}
}

 

In this, simply a HTTP Connection was established and text extracted using “element_name”.text() from inside the specific tags using identifiers like classes or ids. The tags from which the information was to be extracted were identified after exploring the web page’s HTML source code.

The result thus obtained is in the form of a JSON Object

{
  "Wordpress blog: https://loklaknet.wordpress.com/": [
    {
      "posted_on": "June 19, 2016",
      "blog_url": "https://loklaknet.wordpress.com/",
      "author": "shivenmian",
      "title": "loklak_depot u2013 The Beginning: Accounts (Part 3)",
      "content": "So this is my third post in this five part series on loklak_depo... As always, feedback is duly welcome."
    },
    {
      "posted_on": "June 19, 2016",
      "blog_url": "https://loklaknet.wordpress.com/",
      "author": "sopankhosla",
      "title": "Creating a Loklak App!",
      "content": "Hello everyone! Today I will be shifting from course a...ore info refer to the full documentation here. Happy Coding!!!"
    },
    {
      "posted_on": "June 17, 2016",
      "blog_url": "https://loklaknet.wordpress.com/",
      "author": "leonmakk",
      "title": "Loklak Walls Manual Moderation u2013 tweet storage",
      "content": "Loklak walls are going to....Stay tuned for more updates on this new feature of loklak walls!"
    },
    {
      "posted_on": "June 17, 2016",
      "blog_url": "https://loklaknet.wordpress.com/",
      "author": "Robert",
      "title": "Under the hood: Authentication (login)",
      "content": "In the second post of .....key login is ready."
    },
    {
      "posted_on": "June 17, 2016",
      "blog_url": "https://loklaknet.wordpress.com/",
      "author": "jigyasa",
      "title": "Loklak gives some hackernews now !",
      "content": "It's been befittingly said  u... Also, Stay tuned for more posts on data crawling and parsing for Loklak. Feedback and Suggestions welcome"
    },
    {
      "posted_on": "June 16, 2016",
      "blog_url": "https://loklaknet.wordpress.com/",
      "author": "Damini",
      "title": "Does tweets have emotions?",
      "content": "Tweets do intend some kind o...t of features: classify(feat1,u2026,featN) = argmax(P(cat)*PROD(P(featI|cat)"
    },
    {
      "posted_on": "June 15, 2016",
      "blog_url": "https://loklaknet.wordpress.com/",
      "author": "sudheesh001",
      "title": "Dockerize the loklak server and publish docker images to IBM Containers on Bluemix Cloud",
      "content": "Docker is an open source...nd to create and deploy instantly as well as scale on demand."
    }
  ]
}

 

The next step now would include "writeToBackend"-ing and then parsing the JSONObject as desired.

Feel free to ask questions regarding the above code snippet, shall be happy to assist.

Feedback and Suggestions welcome 🙂

Now get wordpress blog updates with Loklak !

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)