package org.shujito.ucs.models;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
import javax.ws.rs.HeaderParam;
import javax.ws.rs.core.Response.Status;
import org.apache.commons.validator.routines.EmailValidator;
import org.shujito.ucs.ApiException;
import org.shujito.ucs.Constants;
import org.shujito.ucs.Crypto;
import org.shujito.ucs.db.Database;
import com.google.gson.annotations.SerializedName;
public class User
{
public static class Validation
{
public final boolean username;
public final boolean password;
public final boolean email;
public Validation()
{
this.username = false;
this.password = false;
this.email = false;
}
public Validation(boolean username, boolean password, boolean email)
{
this.username = username;
this.password = password;
this.email = email;
}
}
public static final String TAG = User.class.getSimpleName();
public static final String TABLE = "users";
public static final String UUID = "uuid";
public static final String CREATED_AT = "created_at";
public static final String UPDATED_AT = "updated_at";
public static final String DELETED_AT = "deleted_at";
public static final String USERNAME = "username";
public static final String DISPLAY_NAME = "display_name";
public static final String PASSWORD = "password";
public static final String PASSWORD_SALT = "password_salt";
public static final String EMAIL = "email";
public static List<User> getAll() throws Exception
{
try (Statement smt = Database.createStatement())
{
try (ResultSet rs = smt
.executeQuery("select uuid,created_at,display_name as username from users where deleted_at is null order by username asc"))
{
List<User> users = new ArrayList<>();
while (rs.next())
{
User user = User.fromResultSet(rs);
users.add(user);
}
return users;
}
}
}
public static User fromUuid(String uuid) throws Exception
{
try (PreparedStatement smt = Database
.prepareStatement("select created_at,display_name as username from users where uuid = ? and deleted_at is null"))
{
smt.setString(1, uuid);
try (ResultSet rs = smt.executeQuery())
{
if (rs.next())
{
User user = User.fromResultSet(rs);
return user;
}
}
throw new ApiException(Constants.Strings.USER_DOES_NOT_EXIST, Status.NOT_FOUND.getStatusCode());
}
}
public static User fromResultSet(ResultSet rs) throws Exception
{
ResultSetMetaData rsmd = rs.getMetaData();
User user = new User();
int count = rsmd.getColumnCount();
for (int idx = 1; idx <= count; idx++)
{
switch (rsmd.getColumnLabel(idx))
{
case UUID:
user.uuid = rs.getString(idx);
break;
case CREATED_AT:
user.createdAt = rs.getLong(idx);
break;
case UPDATED_AT:
user.updatedAt = rs.getLong(idx);
break;
case DELETED_AT:
user.deletedAt = rs.getLong(idx);
break;
case USERNAME:
user.username = rs.getString(idx);
break;
case DISPLAY_NAME:
user.displayName = rs.getString(idx);
break;
case PASSWORD:
user.password = rs.getString(idx);
break;
case EMAIL:
user.email = rs.getString(idx);
break;
}
}
return user;
}
@SerializedName(UUID)
private String uuid;
@SerializedName(CREATED_AT)
private Long createdAt;
@SerializedName(UPDATED_AT)
private Long updatedAt;
@SerializedName(DELETED_AT)
private Long deletedAt;
@SerializedName(USERNAME)
private String username;
@SerializedName(DISPLAY_NAME)
private String displayName;
@SerializedName(EMAIL)
private String email;
@SerializedName(PASSWORD)
private String password;
private Exception exception;
public User()
{
}
public User(@HeaderParam("access-token") String accessToken, @HeaderParam("user-agent") String userAgent) throws Exception
{
if (accessToken == null)
{
this.exception = new ApiException(Constants.Strings.ACCESS_DENIED, Status.FORBIDDEN.getStatusCode());
return;
}
if (accessToken.length() != 44)
{
this.exception = new ApiException(Constants.Strings.MALFORMED_ACCESS_TOKEN, Status.FORBIDDEN.getStatusCode());
return;
}
byte[] tokenBytes = Crypto.base64decode(accessToken);
try (PreparedStatement psm = Database
.prepareStatement("select "
+ "users.uuid,"
+ "users.created_at,"
+ "users.updated_at,"
+ "users.username,"
+ "users.display_name,"
+ "users.email"
+ " from users"
+ " inner join sessions"
+ " on users.uuid=sessions.user_uuid"
+ " where users.deleted_at is null"
+ " and datetime(sessions.expires_at/1000,'unixepoch','localtime') > datetime('now','localtime')"
+ " and sessions.access_token=?"))
{
psm.setBytes(1, tokenBytes);
try (ResultSet rs = psm.executeQuery())
{
if (!rs.next())
throw new ApiException(Constants.Strings.ACCESS_DENIED, Status.FORBIDDEN.getStatusCode());
this.loadResultSet(rs);
}
}
catch (Exception ex)
{
this.exception = ex;
}
}
public String getUuid()
{
return this.uuid;
}
public Long getCreatedAt()
{
return this.createdAt;
}
public Long getUpdatedAt()
{
return this.updatedAt;
}
public Long getDeletedAt()
{
return this.deletedAt;
}
public String getUsername()
{
return this.username;
}
public String getDisplayName()
{
return this.displayName;
}
public String getEmail()
{
return this.email;
}
public String getPassword()
{
return this.password;
}
private void loadResultSet(ResultSet rs) throws Exception
{
User user = User.fromResultSet(rs);
this.uuid = user.uuid;
this.createdAt = user.createdAt;
this.updatedAt = user.updatedAt;
this.deletedAt = user.deletedAt;
this.username = user.username;
this.displayName = user.displayName;
}
public void validate(Validation validate)
{
if (validate.username && this.username == null)
throw new ApiException(Constants.Strings.NO_USERNAME_SPECIFIED, Status.NOT_ACCEPTABLE.getStatusCode());
if (validate.password && this.password == null)
throw new ApiException(Constants.Strings.NO_PASSWORD_SPECIFIED, Status.NOT_ACCEPTABLE.getStatusCode());
if (validate.email && this.email == null)
throw new ApiException(Constants.Strings.NO_EMAIL_SPECIFIED, Status.NOT_ACCEPTABLE.getStatusCode());
// lengths
if (validate.password && this.password.length() < 10)
throw new ApiException(Constants.Strings.PASSWORD_IS_TOO_SHORT, Status.NOT_ACCEPTABLE.getStatusCode());
// formats
if (validate.username && !Pattern.matches("[a-zA-Z]{2,24}", this.username))
throw new ApiException(Constants.Strings.USERNAME_CAN_ONLY_CONTAIN_BETWEEN_2_AND_24_LETTERS, Status.NOT_ACCEPTABLE.getStatusCode());
if (validate.email && !EmailValidator.getInstance().isValid(this.email))
throw new ApiException(Constants.Strings.INVALID_EMAIL_ADDRESS, Status.NOT_ACCEPTABLE.getStatusCode());
}
public void continueOrThrow() throws Exception
{
if (this.exception != null)
throw this.exception;
}
public void load() throws Exception
{
try (PreparedStatement psm = Database.prepareStatement("select "
+ "uuid,"
+ "created_at,"
+ "updated_at,"
+ "deleted_at,"
+ "username,"
+ "display_name"
+ " from users where username = lower(?) and deleted_at is null"))
{
psm.setString(1, this.username);
try (ResultSet rs = psm.executeQuery())
{
if (!rs.next())
throw new ApiException(Constants.Strings.USER_DOES_NOT_EXIST, Status.NOT_FOUND.getStatusCode());
this.loadResultSet(rs);
}
}
}
public void save() throws Exception
{
try (PreparedStatement psm = Database.prepareStatement("insert into users(username,display_name,email) values(lower(?),?,?)"))
{
psm.setString(1, this.username);
psm.setString(2, this.username);
psm.setString(3, this.email);
psm.executeUpdate();
}
catch (SQLException ex)
{
throw new ApiException(ex.getMessage(), Status.CONFLICT.getStatusCode());
}
try (PreparedStatement psm = Database.prepareStatement("select uuid from users where username=lower(?)"))
{
psm.setString(1, this.username);
try (ResultSet rs = psm.executeQuery())
{
if (rs.next())
this.uuid = rs.getString("uuid");
}
}
}
}