loklak_depot – The Beginning: Accounts (Part 1)

The next few posts from my side will be on loklak_depot (name tentative), a new product that we’re trying to build, and the services it may provide, and our steps in that direction.

loklak_depot aims to be a marketplace for all things loklak. Call it DiffBot, but only that it’s much more. Our aim is to offer the crawling services of loklak, the dataset dumps and the APIs to authenticated users in the community. We could use the datasets for various purposes. For a university loklak peer for example, we could have a large dataset with special access for students. For shops, we could have datasets tailored for customer use. We also have the apps system which we could use to offer various other services tailored for the user, or commercially sell them like the crawlers at DiffBot. All of these features, at one place: loklak_depot.

For the initial approach to loklak_depot, it was decided to implement an accounts system, such that authenticated users can access features that they cannot on a normal peer. This grants them special rights using a process called provisioning. The provisioning system is implemented through a concept called AAA: Authentication, Authorization and Accounting.

Authentication asks the question, “Who or what are you?”
Authorization asks, “What are you allowed to do?”
Accounting wants to know, “What did you do?”

AAA is the basis of the permissions and the accounts system, and will be used in further blog posts as well, so make a note of it.

In the accounts system, I have implemented both sign-up and login page as apps, and coded the backend logic in Java. I will cover the sign-up page implementation in this post.

The sign-up page validates the user’s email ID and password and signs him up to loklak. Validation for email was simple regex. For password, there was a counter which specified the strength of the password, also passwords below 6 letters were not accepted and they had to be a mixture of alphanums, letters and numbers. A small code snippet of the strength level is shown below: (written in JQuery)


function strengthlvl(pass){

        var strength = 0;
        $('#passtrength').removeClass();
        if(pass.length == 0){
            return "";
        }
        if(pass.length < 6){ $('#passtrength').addClass("error");             passerr = true;             return "Too short";         }         if (pass.length >=7) { //sufficient length
            strength += 1;
        }
        if (pass.match(/([a-z].*[A-Z])|([A-Z].*[a-z])/)){ //both uppercase and lowercase
            strength += 1;
        }
        if (pass.match(/([a-zA-Z])/) && pass.match(/([0-9])/)){ //both letters and nums
            strength += 1; 
        }
        if (pass.match(/([^a-zA-Z0-9])/)){ //alphanumeric
            strength += 1;
        }

        if (pass.match(/(.*[^a-zA-Z0-9].*[^a-zA-Z0-9])/)){ //more than two alphanumeric chars
            strength += 1;
        }

        passerr = false;
        $('#pass').removeClass();
        $('#passtrength').removeClass();
        if (strength < 2 )         {               $('#passtrength').css('color', 'orange'); // color changes             return "Weak";                    }         else if (strength >= 2 && strength < 4)
        {
            $('#passtrength').css('color', 'LightGreen');
            return "Good";       
        }
        else 
        {
            $('#passtrength').css('color', 'GreenYellow');
            return "Strong";
        }    

    }

The data from the signup page is POSTed to the database (an authentication.json file in the data folder) via a servlet. The posting was achieved as follows: (code snippets)

JQuery:


$.post( "/api/signup.json", { signup: mail, password: pwd }, function(data) {
                console.log(data.status);
                console.log(data.reason);
                alert(data.status + ", " + data.reason);
            }, "json" );

Java Servlet: (SignUpServlet.java)


@Override
    public JSONObject serviceImpl(Query post, Authorization rights) throws APIException {

    	JSONObject result = new JSONObject();
    	
    	if(!rights.isAdmin() && !"true".equals(DAO.getConfig("users.public.signup", "false"))){
    		result.put("status", "error");
    		result.put("reason", "Public signup disabled");
    		return result;
    	}
    	
    	if(post.get("signup",null) == null || post.get("password", null) == null){
    		result.put("status", "error");
    		result.put("reason", "signup or password empty");
    		return result;
    	}
    	
    	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) {
			result.put("status", "error");
    		result.put("reason", "malformed query");
    		return result;
		}
    	
    	
    	ClientCredential credential = new ClientCredential(ClientCredential.Type.passwd_login, signup);
    	if (DAO.authentication.has(credential.toString())) {
    		result.put("status", "error");
    		result.put("reason", "email already taken");
    		return result;
    	}
    	
    	JSONObject user_obj = new JSONObject();
    	String salt = createRandomString(20);
    	user_obj.put("salt", salt);
    	user_obj.put("passwordHash", getHash(password, salt));
    	ClientIdentity identity = new ClientIdentity(ClientIdentity.Type.email, credential.getName());
    	user_obj.put("id",identity.toString());
        DAO.authentication.put(credential.toString(), user_obj, credential.isPersistent());
    	
    	result.put("status", "ok");
		result.put("reason", "ok");
		return result;
    }

It must be noted that in the SignUpServlet code, the class extended AbstractAPIHandler.java (a custom class we implemented to cover User Identities, the AAA concept code, as well as the code for receiving the POST requests), and the API Path was set to "/api/signup.json".

Once the information is POSTed to the servlet, it returns a JSON containing a status and a reason depending on the situation. In normal conditions, both have the value ok.

For security, passwords were hashed in SHA256 using salts, and then the hashed passwords are stored in the file. Also, the email and password sent are encoded (from the client side) in URI using encodeURIComponent and sent to the servlet, and decoded back using URLDecoder.decode(post.get("signup",null),"UTF-8"); . The purpose of this was because SHA can throw an exception for characters not in UTF-8, such as those used in foreign languages.

Once this was done, the servlet was added on the MainActivity i.e LoklakServer.java:

servletHandler.addServlet(SignUpServlet.class, "/api/signup.json").

and the page worked like a charm.

Through this, I became really familiar with using servlets first-hand, and coding in JQuery, things I never coded before. I feel really comfortable with using both of them now.

What next in this? I’m considering rewriting the client-side code in Angular so that we can have a Sign Up option on the top menu on the homepage of every loklak peer, instead of as an app. I shall be doing this at a later stage.

In my next two posts, I’ll cover the implementation of the login page, as well as email services for Password Recovery and User Verification. Feedback and suggestions welcome. 🙂

loklak_depot – The Beginning: Accounts (Part 1)