Skip to content

Commit

Permalink
#9 Added support for user account creation and login mechanisms to av…
Browse files Browse the repository at this point in the history
…oid providing tokens to unauthorized users.
  • Loading branch information
tigreped committed Nov 25, 2015
1 parent 6e5466f commit 02b6736
Show file tree
Hide file tree
Showing 7 changed files with 168 additions and 36 deletions.
44 changes: 44 additions & 0 deletions src/main/java/br/gov/sibbr/api/controller/InterfaceController.java
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,44 @@ public String login(LoginForm loginForm, Model model) {
return "login_fail";
}

// Method responsible for managing occurrence requests
@RequestMapping(value = "/register", method = RequestMethod.POST)
public String register(LoginForm loginForm, Model model) {
String email = loginForm.getEmail();
String password = loginForm.getPassword();
String passwordCheck = loginForm.getPasswordCheck();
if (password != null || passwordCheck != null) {
if (email == null || email.contains("@")) {
// Check if both passwords are equal:
if (password.equalsIgnoreCase(passwordCheck)) {
if (password.length() >= 8) {
String message = authService.createAccount(email, password);
model.addAttribute("success", message);
}
// Password too small:
else {
model.addAttribute("error",
"Password too small. Password must be at least 5 characters long, with a valid address.");
}
}
// Passwords don't match
else {
model.addAttribute("error",
"The passwords don't match. Try again, and make sure the same password is entered in both password and password verification fields.");
}
}
// Invalid e-mail information
else {
model.addAttribute("error", "Invalid e-mail. Try again, with a valid address.");
}
}
// One of the fields was left blank:
else {
model.addAttribute("error", "You must provide a valid password and repeat it on the Veirification field. Please, try again.");
}
return "register";
}

// Method responsible for managing occurrence requests
@RequestMapping(value = "/admin", method = RequestMethod.POST)
public String admin(LoginForm loginForm, Model model) {
Expand Down Expand Up @@ -90,6 +128,12 @@ public String login() {
return "login";
}

// Method responsible for calling the registration template
@RequestMapping(value = "/register", method = RequestMethod.GET)
public String register() {
return "register";
}

// Method responsible for calling the login template
@RequestMapping(value = "/admin", method = RequestMethod.GET)
public String admin() {
Expand Down
20 changes: 18 additions & 2 deletions src/main/java/br/gov/sibbr/api/db/DatabaseAuth.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ public class DatabaseAuth {
public static final String API_TOKEN_AUTO_ID = "auto_id";
public static final String API_TOKEN_TOKEN = "token";

public static final int USER_EXISTS = -2;

/**
* Default class constructor, creates a new connection to the database
*/
Expand Down Expand Up @@ -85,8 +87,7 @@ public ResultSet queryApiUser(String email) {
Statement statement = null;
try {
statement = conn.createStatement();
resultSet = statement.executeQuery("SELECT " + API_USER_PASSWORD + ", " + API_USER_EMAIL + ", "
+ API_USER_SALT + " FROM " + API_USER_TABLE + " WHERE " + API_USER_EMAIL + " = \'" + email + "\'");
resultSet = statement.executeQuery("SELECT * FROM " + API_USER_TABLE + " WHERE " + API_USER_EMAIL + " = \'" + email + "\'");
} catch (SQLException e) {
e.printStackTrace();
}
Expand Down Expand Up @@ -171,4 +172,19 @@ public int updateApiUserToken(String email, Long auto_id) {
}
return update;
}

public int createApiUser(String email, String password, String salt) {
Statement statement = null;
int result = 0;
// New user, insert into the database
try {
statement = conn.createStatement();
String sql = "INSERT INTO " + API_USER_TABLE + " (" + API_USER_EMAIL + ", " + API_USER_PASSWORD + ", "
+ API_USER_SALT + ") values (\'" + email + "\', \'" + password + "\', \'" + salt + "\')";
result = statement.executeUpdate(sql);
} catch (SQLException e) {
e.printStackTrace();
}
return result;
}
}
11 changes: 10 additions & 1 deletion src/main/java/br/gov/sibbr/api/model/LoginForm.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
package br.gov.sibbr.api.model;

/**
* POJO that encapsulates login form data
* POJO that encapsulates login and register form data
*
* @author Pedro Guimarães
*
Expand All @@ -25,6 +25,7 @@ public class LoginForm {

private String email;
private String password;
private String passwordCheck;

public String getEmail() {
return email;
Expand All @@ -41,4 +42,12 @@ public String getPassword() {
public void setPassword(String password) {
this.password = password;
}

public String getPasswordCheck() {
return passwordCheck;
}

public void setPasswordCheck(String passwordCheck) {
this.passwordCheck = passwordCheck;
}
}
97 changes: 75 additions & 22 deletions src/main/java/br/gov/sibbr/api/service/AuthService.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@

/**
* Service class for all authentication related methods
*
* @author Pedro Guimarães
*
*/
Expand Down Expand Up @@ -100,34 +101,42 @@ public String checkPassword(String email, String password) {
ResultSet rs = dba.queryApiUser(email);
// Check if there is at least one result
if (rs != null) {
HashMap<String, String> hashMap = processApiUser(rs);
String databasePassword = hashMap.get("password");
String databaseSalt = hashMap.get("salt");
String databaseEmail = hashMap.get("email");
HashMap<String, Object> hashMap = processApiUser(rs);
String databasePassword = (String) hashMap.get("password");
String databaseSalt = (String) hashMap.get("salt");
String databaseEmail = (String) hashMap.get("email");
Boolean authorized = (Boolean) hashMap.get("authorized");
// The user has been found on the system
if (databaseEmail != null) {
// There is valid salt
if (databaseSalt != null) {
// Concatenate user provided hashed password with salt:
String saltedPassword = passwordHash + databaseSalt;
// Rehash hashed password now concatenated with salt:
String saltedPasswordHash = hash(saltedPassword, SHA256);
// Compare the provided hashed password with the database
// password
if (databasePassword.equalsIgnoreCase(saltedPasswordHash))
return null;
return "Wrong password.";
// The user is authorized
if (authorized) {
// There is valid salt
if (databaseSalt != null) {
// Concatenate user provided hashed password with salt:
String saltedPassword = passwordHash + databaseSalt;
// Rehash hashed password now concatenated with salt:
String saltedPasswordHash = hash(saltedPassword, SHA256);
// Compare the provided hashed password with the
// database
// password
if (databasePassword.equalsIgnoreCase(saltedPasswordHash))
return null;
return "Wrong password.";
}
// This should not happen, salt should be always created
// upon
// new api user
return "No salt for this email address.";
}
// This should not happen, salt should be always created upon
// new api user
return "No salt for this email address.";
return "Unauthorized user. API admin must authorize your account in order to enable your access to tokens.";
}
}
return "Invalid e-mail address.";
}

/**
* Check if the provided token is valid and not expired.
*
* @param token
* @return error messages or null if the token is valid.
*/
Expand Down Expand Up @@ -213,16 +222,18 @@ public String fetchToken(String email) {
* database query from DatabaseAuth.queryApiUser()
* @return
*/
public HashMap<String, String> processApiUser(ResultSet rs) {
HashMap<String, String> hashMap = new HashMap<String, String>();
public HashMap<String, Object> processApiUser(ResultSet rs) {
HashMap<String, Object> hashMap = new HashMap<String, Object>();
try {
while (rs.next()) {
String password = Utils.getString(rs, "password");
String salt = Utils.getString(rs, "salt");
String email = Utils.getString(rs, "email");
Boolean authorized = rs.getBoolean("authorized");
hashMap.put("password", password);
hashMap.put("salt", salt);
hashMap.put("email", email);
hashMap.put("authorized", authorized);
}
} catch (SQLException e) {
e.printStackTrace();
Expand Down Expand Up @@ -276,7 +287,7 @@ public HashMap<String, Object> processApiToken(ResultSet rs) {
}

/**
* generates a new token hash, inserts it into the api_token table and
* Generates a new token hash, inserts it into the api_token table and
* updates the api_user table to point to the token record's foreign key
*
* @param email
Expand All @@ -298,6 +309,19 @@ public String generateNewToken(String email) {
return null;
}

/**
* Generates a new md5 hash to be the user account password salteign key
*
* @param email
* @return
*/
public String generateSalt() {
long random = new Random().nextLong();
// Generate a pseudorandom hash for the salt
String hash = hash(Long.toString(System.currentTimeMillis()) + Long.toString(random), MD5);
return hash;
}

/**
* Receives a resultset to fetch and return the auto_id from a api_token
* record
Expand All @@ -318,4 +342,33 @@ public Long processTokenAutoId(ResultSet rs) {
}
return auto_id;
}

public String createAccount(String email, String password) {
// Check if user already exists in the database:
ResultSet rs = dba.queryApiUser(email);
if (rs != null) {
HashMap<String, Object> hashMap = processApiUser(rs);
String databaseEmail = (String)hashMap.get("email");
if (databaseEmail != null) {
if (databaseEmail.equalsIgnoreCase(email)) {
return "Error. User already registered to the database.";
}
}
}
// New valid email user, carry on:
// Generate salt for the user account
String salt = generateSalt();
// Turn the provided form password into a hash
String passwordHash = hash(password, SHA256);
// Concatenate user provided hashed password with salt:
String saltedPassword = passwordHash + salt;
// Rehash hashed password now concatenated with salt:
String saltedPasswordHash = hash(saltedPassword, SHA256);
int result = dba.createApiUser(email, saltedPasswordHash, salt);
if (result == 0) {
return "Error inserting user to the database.";
}
return "New account created successfully to the user " + email
+ ". Once the admin authorizes your account you will receive a valid token upon login.";
}
}
2 changes: 1 addition & 1 deletion src/main/resources/application.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Api version
server.contextPath=/v1.1
server.contextPath=/v1.2
# SSL set up:
server.port=8444
server.ssl.key-store=keystore.p12
Expand Down
10 changes: 0 additions & 10 deletions src/main/resources/templates/greetings.html

This file was deleted.

20 changes: 20 additions & 0 deletions src/main/resources/templates/register.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
<head>
<title>Registration page</title>
</head>
<body>
<div th:if="${error != null}" th:text="'Error: ' + ${error}"></div>
<div th:if="${success != null}" th:text="${success}"></div>
<div th:if="${success == null}">
<p>Please provide a valid e-mail account and a password with at least 8 characters to create your account:</p>
<form th:action="@{/register}" method="post">
<div><label> E-mail: <input type="text" name="email"/> </label></div>
<div><label> Password: <input type="password" name="password"/> </label></div>
<div><label> Verify password: <input type="password" name="passwordCheck"/> </label></div>
<div><input type="submit" value="Create account"/></div>
</form>
</div>
</body>
</html>

0 comments on commit 02b6736

Please sign in to comment.