Implement RESTful API, JWT auth, SQLite storage
This update brings a huge change to the whole system's structure. A new RESTful API has been implemented, which allows users to register, login and store data. The API only supports HTTP POST, and can be accessed via /api/v1/. Requests must contain a JSON body with the necessary entries, which are: /api/v1/register AND /api/v1/login: { "username": "username", "password": "password", "encoding": "plaintext/base64" } (Note: passwords can be encoded via "base64" or "plaintext".) /api/v1/store: { "jwt": "encrypted_key_here", "url": "https://google.com/" } The flow is: - register via /api/v1/register; - login via /api/v1/login, listen for JWT token in response; - store via /api/v1/store, by sending JWT and URL to store. The SQLite database now has 2 tables, "users" and "history". The "users" table is used to store user data: - username; - password, secured via bcrypt; - random user UUID. The "history" table is used to store browsing history: - user UUID, to identify the user; - browsed url. The secret used to sign JWTs is stored in the config.yml file. Other new features include SQL-injection protection, multiple validity/security checks on usernames and passwords, etc. Signed-off-by: Lorenzo Dellacà <lorenzo.dellaca@mind-overflow.net>
This commit is contained in:
parent
ce172c3dc4
commit
07ec036e4f
19
pom.xml
19
pom.xml
@ -10,6 +10,7 @@
|
|||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
<!-- todo: clean up this mess -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>ro.pippo</groupId>
|
<groupId>ro.pippo</groupId>
|
||||||
<artifactId>pippo</artifactId>
|
<artifactId>pippo</artifactId>
|
||||||
@ -37,8 +38,24 @@
|
|||||||
<artifactId>snakeyaml</artifactId>
|
<artifactId>snakeyaml</artifactId>
|
||||||
<version>1.21</version>
|
<version>1.21</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>commons-codec</groupId>
|
||||||
|
<artifactId>commons-codec</artifactId>
|
||||||
|
<version>1.14</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>at.favre.lib</groupId>
|
||||||
|
<artifactId>bcrypt</artifactId>
|
||||||
|
<version>0.9.0</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.auth0</groupId>
|
||||||
|
<artifactId>java-jwt</artifactId>
|
||||||
|
<version>3.10.3</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
<build>
|
<build>
|
||||||
<plugins>
|
<plugins>
|
||||||
<plugin>
|
<plugin>
|
||||||
|
@ -1,18 +1,13 @@
|
|||||||
package net.mindoverflow.webmarker;
|
package net.mindoverflow.webmarker;
|
||||||
|
|
||||||
import net.mindoverflow.webmarker.runnables.StatsRunnable;
|
|
||||||
import net.mindoverflow.webmarker.utils.Cached;
|
import net.mindoverflow.webmarker.utils.Cached;
|
||||||
import net.mindoverflow.webmarker.utils.sql.SQLiteManager;
|
|
||||||
import net.mindoverflow.webmarker.webserver.WebApplication;
|
|
||||||
import net.mindoverflow.webmarker.utils.config.ConfigEntries;
|
import net.mindoverflow.webmarker.utils.config.ConfigEntries;
|
||||||
import net.mindoverflow.webmarker.utils.config.ConfigManager;
|
import net.mindoverflow.webmarker.utils.config.ConfigManager;
|
||||||
import net.mindoverflow.webmarker.utils.messaging.Messenger;
|
import net.mindoverflow.webmarker.utils.messaging.Messenger;
|
||||||
|
import net.mindoverflow.webmarker.utils.sql.SQLiteManager;
|
||||||
|
import net.mindoverflow.webmarker.webserver.WebApplication;
|
||||||
import ro.pippo.core.Pippo;
|
import ro.pippo.core.Pippo;
|
||||||
|
|
||||||
import java.util.concurrent.Executors;
|
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
public class WebMarker {
|
public class WebMarker {
|
||||||
|
|
||||||
private static final Messenger msg = new Messenger();
|
private static final Messenger msg = new Messenger();
|
||||||
@ -33,7 +28,9 @@ public class WebMarker {
|
|||||||
pippo.start(port);
|
pippo.start(port);
|
||||||
msg.info("Started webserver.");
|
msg.info("Started webserver.");
|
||||||
|
|
||||||
|
/* todo: enable to track ram usage
|
||||||
ScheduledExecutorService exec = Executors.newSingleThreadScheduledExecutor();
|
ScheduledExecutorService exec = Executors.newSingleThreadScheduledExecutor();
|
||||||
exec.scheduleAtFixedRate(new StatsRunnable(), 0, 5, TimeUnit.SECONDS);
|
exec.scheduleAtFixedRate(new StatsRunnable(), 0, 5, TimeUnit.SECONDS);
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,14 @@
|
|||||||
|
package net.mindoverflow.webmarker.utils;
|
||||||
|
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import com.google.gson.JsonParser;
|
||||||
|
|
||||||
|
public class FileUtils {
|
||||||
|
|
||||||
|
|
||||||
|
public static JsonObject stringToJson(String body)
|
||||||
|
{
|
||||||
|
JsonParser jsonParser = new JsonParser();
|
||||||
|
return (JsonObject) jsonParser.parse(body);
|
||||||
|
}
|
||||||
|
}
|
@ -1,55 +0,0 @@
|
|||||||
package net.mindoverflow.webmarker.utils;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class URLMap {
|
|
||||||
|
|
||||||
private static List<Integer> ids = new ArrayList<>();
|
|
||||||
private static List<String> urls = new ArrayList<>();
|
|
||||||
|
|
||||||
public static void saveUrl(int userId, String url)
|
|
||||||
{
|
|
||||||
ids.add(userId);
|
|
||||||
urls.add(url);
|
|
||||||
System.out.println();
|
|
||||||
System.out.println("Saved ID: " + userId + "; URL: " + url);
|
|
||||||
System.out.println("table is now:");
|
|
||||||
for(int pos = 0; pos < ids.size(); pos++)
|
|
||||||
{
|
|
||||||
System.out.println("ID = " + ids.get(pos) + "; URL = " + urls.get(pos));
|
|
||||||
}
|
|
||||||
System.out.println();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static List<String> getUserUrls(int userId)
|
|
||||||
{
|
|
||||||
List<String>thisUserUrls = new ArrayList<>();
|
|
||||||
for(int pos = 0; pos < ids.size(); pos++)
|
|
||||||
{
|
|
||||||
if(userId == ids.get(pos))
|
|
||||||
{
|
|
||||||
thisUserUrls.add(urls.get(pos));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return thisUserUrls;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void dropUser(int userId)
|
|
||||||
{
|
|
||||||
List<Integer> newIds = new ArrayList<>();
|
|
||||||
List<String> newUrls = new ArrayList<>();
|
|
||||||
for(int pos = 0; pos < ids.size(); pos++)
|
|
||||||
{
|
|
||||||
if(ids.get(pos) != userId)
|
|
||||||
{
|
|
||||||
newIds.add(ids.get(pos));
|
|
||||||
newUrls.add(urls.get(pos));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ids = newIds;
|
|
||||||
urls = newUrls;
|
|
||||||
}
|
|
||||||
}
|
|
@ -2,7 +2,10 @@ package net.mindoverflow.webmarker.utils.config;
|
|||||||
|
|
||||||
public enum ConfigEntries
|
public enum ConfigEntries
|
||||||
{
|
{
|
||||||
WEBSERVER_PORT("port", 7344);
|
WEBSERVER_PORT("port", 7344),
|
||||||
|
JWT_SECRET("secret", "changeMe"),
|
||||||
|
|
||||||
|
;
|
||||||
|
|
||||||
private final String path;
|
private final String path;
|
||||||
private Object value;
|
private Object value;
|
||||||
|
@ -64,7 +64,10 @@ public class ConfigManager
|
|||||||
msg.sendLine(MessageLevel.NONE, "OK");
|
msg.sendLine(MessageLevel.NONE, "OK");
|
||||||
|
|
||||||
for(ConfigEntries entry : ConfigEntries.values())
|
for(ConfigEntries entry : ConfigEntries.values())
|
||||||
{ msg.info(entry.name() + ": " + entry.getValue()); }
|
{
|
||||||
|
if(entry == ConfigEntries.JWT_SECRET) continue; // we don't want to log encryption secret key
|
||||||
|
msg.info(entry.name() + ": " + entry.getValue());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getJarAbsolutePath()
|
public static String getJarAbsolutePath()
|
||||||
|
@ -0,0 +1,65 @@
|
|||||||
|
package net.mindoverflow.webmarker.utils.security;
|
||||||
|
|
||||||
|
import at.favre.lib.crypto.bcrypt.BCrypt;
|
||||||
|
import org.apache.commons.codec.binary.Hex;
|
||||||
|
|
||||||
|
import javax.crypto.Mac;
|
||||||
|
import javax.crypto.spec.SecretKeySpec;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.security.InvalidKeyException;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.util.Base64;
|
||||||
|
|
||||||
|
public class EncryptionUtils {
|
||||||
|
public static String hmacSha256(String key, String value)
|
||||||
|
{
|
||||||
|
byte[] keyBytes = key.getBytes();
|
||||||
|
SecretKeySpec signingKey = new SecretKeySpec(keyBytes, "HmacSHA256");
|
||||||
|
|
||||||
|
try {
|
||||||
|
Mac mac = Mac.getInstance("HmacSHA256");
|
||||||
|
mac.init(signingKey);
|
||||||
|
|
||||||
|
byte[] rawHmac = mac.doFinal(value.getBytes());
|
||||||
|
|
||||||
|
byte[] hexBytes = new Hex().encode(rawHmac);
|
||||||
|
|
||||||
|
return new String(hexBytes, "UTF-8");
|
||||||
|
} catch (NoSuchAlgorithmException | InvalidKeyException | UnsupportedEncodingException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String bcrypt(String value)
|
||||||
|
{
|
||||||
|
return BCrypt.withDefaults().hashToString(12, value.toCharArray()); // todo: custom salt
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean bcryptMatches(String storedValue, String unencrypted)
|
||||||
|
{
|
||||||
|
BCrypt.Result result = BCrypt.verifyer().verify(unencrypted.toCharArray(), storedValue);
|
||||||
|
return result.verified;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String handleEncoding(String encoding, String encodedPassword)
|
||||||
|
{
|
||||||
|
String password;
|
||||||
|
|
||||||
|
switch (encoding.toLowerCase())
|
||||||
|
{
|
||||||
|
case "plaintext":
|
||||||
|
password = encodedPassword;
|
||||||
|
break;
|
||||||
|
case "base64":
|
||||||
|
password = new String(Base64.getDecoder().decode(encodedPassword));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
password = "";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return password;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
package net.mindoverflow.webmarker.utils.security;
|
||||||
|
|
||||||
|
public class SafetyCheck {
|
||||||
|
|
||||||
|
public static boolean isSafeUsername(String username)
|
||||||
|
{
|
||||||
|
// todo: allow configuration
|
||||||
|
if(!username.matches("[a-zA-Z0-9]*")) return false;
|
||||||
|
if(username.length() > 15) return false;
|
||||||
|
if(username.length() < 3) return false;
|
||||||
|
if(username.equalsIgnoreCase("null")) return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isSafePassword(String password)
|
||||||
|
{
|
||||||
|
if(password.length() < 6) return false;
|
||||||
|
if(password.getBytes().length > 71) return false; // see https://github.com/patrickfav/bcrypt#handling-for-overlong-passwords
|
||||||
|
|
||||||
|
// todo: more password security
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isValidEncoding(String encoding)
|
||||||
|
{
|
||||||
|
if(encoding.equalsIgnoreCase("base64")) return true;
|
||||||
|
if(encoding.equalsIgnoreCase("plaintext")) return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
@ -1,26 +0,0 @@
|
|||||||
package net.mindoverflow.webmarker.utils.sql;
|
|
||||||
|
|
||||||
|
|
||||||
import net.mindoverflow.webmarker.utils.sql.primitives.SQLTable;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
|
|
||||||
public enum FDatabaseTable
|
|
||||||
{
|
|
||||||
USERS(new SQLTable("users", // table name
|
|
||||||
new ArrayList<>(){{ // columns
|
|
||||||
add(FDatabaseColumn.USERID);
|
|
||||||
add(FDatabaseColumn.USERNAME);
|
|
||||||
add(FDatabaseColumn.PASSWORD);
|
|
||||||
}})),
|
|
||||||
|
|
||||||
;
|
|
||||||
|
|
||||||
private final SQLTable table;
|
|
||||||
|
|
||||||
FDatabaseTable(SQLTable table)
|
|
||||||
{ this.table = table; }
|
|
||||||
|
|
||||||
public SQLTable getTable()
|
|
||||||
{ return table; }
|
|
||||||
}
|
|
@ -3,14 +3,14 @@ package net.mindoverflow.webmarker.utils.sql;
|
|||||||
import net.mindoverflow.webmarker.utils.sql.primitives.SQLColumn;
|
import net.mindoverflow.webmarker.utils.sql.primitives.SQLColumn;
|
||||||
import net.mindoverflow.webmarker.utils.sql.primitives.SQLDataType;
|
import net.mindoverflow.webmarker.utils.sql.primitives.SQLDataType;
|
||||||
|
|
||||||
public enum FDatabaseColumn
|
public enum MDatabaseColumn
|
||||||
{
|
{
|
||||||
|
|
||||||
ALL(new SQLColumn("*"), null),
|
ALL(new SQLColumn("*"), null),
|
||||||
USERNAME(new SQLColumn("username"), SQLDataType.VARCHAR_128),
|
USERNAME(new SQLColumn("username"), SQLDataType.VARCHAR_128),
|
||||||
PASSWORD(new SQLColumn("password"), SQLDataType.VARCHAR_128),
|
PASSWORD(new SQLColumn("password"), SQLDataType.VARCHAR_128),
|
||||||
USERID(new SQLColumn("userid"), SQLDataType.VARCHAR_128),
|
USER_UUID(new SQLColumn("userid"), SQLDataType.VARCHAR_128),
|
||||||
|
WEB_DOMAIN(new SQLColumn("domain"), SQLDataType.VARCHAR_128),
|
||||||
|
|
||||||
|
|
||||||
;
|
;
|
||||||
@ -18,7 +18,7 @@ public enum FDatabaseColumn
|
|||||||
private final SQLColumn column;
|
private final SQLColumn column;
|
||||||
private final SQLDataType type;
|
private final SQLDataType type;
|
||||||
|
|
||||||
FDatabaseColumn(SQLColumn column, SQLDataType type)
|
MDatabaseColumn(SQLColumn column, SQLDataType type)
|
||||||
{
|
{
|
||||||
this.column = column;
|
this.column = column;
|
||||||
this.type = type;
|
this.type = type;
|
@ -0,0 +1,33 @@
|
|||||||
|
package net.mindoverflow.webmarker.utils.sql;
|
||||||
|
|
||||||
|
|
||||||
|
import net.mindoverflow.webmarker.utils.sql.primitives.SQLTable;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
public enum MDatabaseTable
|
||||||
|
{
|
||||||
|
USERS(new SQLTable("users", // table name
|
||||||
|
new ArrayList<>(){{ // columns
|
||||||
|
add(MDatabaseColumn.USER_UUID);
|
||||||
|
add(MDatabaseColumn.USERNAME);
|
||||||
|
add(MDatabaseColumn.PASSWORD);
|
||||||
|
}})),
|
||||||
|
|
||||||
|
HISTORY(new SQLTable("history",
|
||||||
|
new ArrayList<>(){{
|
||||||
|
add(MDatabaseColumn.USER_UUID);
|
||||||
|
add(MDatabaseColumn.WEB_DOMAIN);
|
||||||
|
|
||||||
|
}}))
|
||||||
|
|
||||||
|
;
|
||||||
|
|
||||||
|
private final SQLTable table;
|
||||||
|
|
||||||
|
MDatabaseTable(SQLTable table)
|
||||||
|
{ this.table = table; }
|
||||||
|
|
||||||
|
public SQLTable getTable()
|
||||||
|
{ return table; }
|
||||||
|
}
|
@ -0,0 +1,82 @@
|
|||||||
|
package net.mindoverflow.webmarker.utils.sql;
|
||||||
|
|
||||||
|
import net.mindoverflow.webmarker.utils.Cached;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public class MarkerSQLUtils {
|
||||||
|
|
||||||
|
public static boolean addUser(UUID randomId, String name, String password)
|
||||||
|
{
|
||||||
|
|
||||||
|
String query = "INSERT INTO " + MDatabaseTable.USERS.getTable().getTableSQLName() + " (" +
|
||||||
|
MDatabaseColumn.USER_UUID.getColumn().getColumnSQLName() + ", " +
|
||||||
|
MDatabaseColumn.USERNAME.getColumn().getColumnSQLName() + ", " +
|
||||||
|
MDatabaseColumn.PASSWORD.getColumn().getColumnSQLName() + ") VALUES (?, ?, ?);";
|
||||||
|
|
||||||
|
return Cached.sqlManager.executeUpdate(query, randomId.toString(), name, password);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean userExists(String name)
|
||||||
|
{
|
||||||
|
String query = "SELECT " + MDatabaseColumn.USERNAME.getColumn().getColumnSQLName() +
|
||||||
|
" FROM " + MDatabaseTable.USERS.getTable().getTableSQLName() +
|
||||||
|
" WHERE " + MDatabaseColumn.USERNAME.getColumn().getColumnSQLName() +
|
||||||
|
" = ? ;";
|
||||||
|
|
||||||
|
List<String> result = Cached.sqlManager.executeStatement(query, MDatabaseColumn.USERNAME, name);
|
||||||
|
return result.size() > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean uuidTaken(UUID randomId)
|
||||||
|
{
|
||||||
|
String query = "SELECT " + MDatabaseColumn.USER_UUID.getColumn().getColumnSQLName() +
|
||||||
|
" FROM " + MDatabaseTable.USERS.getTable().getTableSQLName() +
|
||||||
|
" WHERE " + MDatabaseColumn.USER_UUID.getColumn().getColumnSQLName() +
|
||||||
|
" = ? ;";
|
||||||
|
|
||||||
|
List<String> result = Cached.sqlManager.executeStatement(query, MDatabaseColumn.USER_UUID, randomId.toString());
|
||||||
|
if(result.size() > 0) return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static UUID getUserUUID(String username)
|
||||||
|
{
|
||||||
|
String query = "SELECT " + MDatabaseColumn.USER_UUID.getColumn().getColumnSQLName() +
|
||||||
|
" FROM " + MDatabaseTable.USERS.getTable().getTableSQLName() +
|
||||||
|
" WHERE " + MDatabaseColumn.USERNAME.getColumn().getColumnSQLName() +
|
||||||
|
" = ? ;";
|
||||||
|
|
||||||
|
List<String> result = Cached.sqlManager.executeStatement(query, MDatabaseColumn.USER_UUID, username);
|
||||||
|
if(result.size() != 1) return null; //todo: error!
|
||||||
|
|
||||||
|
return UUID.fromString(result.get(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo: use UUID?
|
||||||
|
public static String getUserBcryptedPassword(String username)
|
||||||
|
{
|
||||||
|
String query = "SELECT " + MDatabaseColumn.PASSWORD.getColumn().getColumnSQLName() +
|
||||||
|
" FROM " + MDatabaseTable.USERS.getTable().getTableSQLName() +
|
||||||
|
" WHERE " + MDatabaseColumn.USERNAME.getColumn().getColumnSQLName() +
|
||||||
|
" = ? ;";
|
||||||
|
|
||||||
|
List<String> result = Cached.sqlManager.executeStatement(query, MDatabaseColumn.PASSWORD, username);
|
||||||
|
if(result.size() != 1) return null; // todo: error!
|
||||||
|
|
||||||
|
return result.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean addHistoryRecord(UUID uuid, String url)
|
||||||
|
{
|
||||||
|
String query = "INSERT INTO " + MDatabaseTable.HISTORY.getTable().getTableSQLName() + " (" +
|
||||||
|
MDatabaseColumn.USER_UUID.getColumn().getColumnSQLName() + ", " +
|
||||||
|
MDatabaseColumn.WEB_DOMAIN.getColumn().getColumnSQLName() + ") VALUES (?, ?);";
|
||||||
|
|
||||||
|
return Cached.sqlManager.executeUpdate(query, uuid.toString(), url);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -7,6 +7,7 @@ import net.mindoverflow.webmarker.utils.sql.primitives.SQLDataType;
|
|||||||
import net.mindoverflow.webmarker.utils.sql.primitives.SQLTable;
|
import net.mindoverflow.webmarker.utils.sql.primitives.SQLTable;
|
||||||
|
|
||||||
import java.sql.*;
|
import java.sql.*;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class SQLiteManager {
|
public class SQLiteManager {
|
||||||
@ -37,17 +38,17 @@ public class SQLiteManager {
|
|||||||
|
|
||||||
private void doInitialSetup()
|
private void doInitialSetup()
|
||||||
{
|
{
|
||||||
for(FDatabaseTable currentTable : FDatabaseTable.values())
|
for(MDatabaseTable currentTable : MDatabaseTable.values())
|
||||||
{
|
{
|
||||||
if(!tableExists(currentTable))
|
if(!tableExists(currentTable))
|
||||||
{
|
{
|
||||||
msg.info("Creating SQLite table `" + currentTable.getTable().getTableSQLName() + "`");
|
msg.info("Creating SQLite table '" + currentTable.getTable().getTableSQLName() + "'");
|
||||||
createTable(currentTable);
|
createTable(currentTable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean tableExists(FDatabaseTable tableEnum)
|
private boolean tableExists(MDatabaseTable tableEnum)
|
||||||
{
|
{
|
||||||
String name = tableEnum.getTable().getTableSQLName();
|
String name = tableEnum.getTable().getTableSQLName();
|
||||||
|
|
||||||
@ -70,16 +71,16 @@ public class SQLiteManager {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createTable(FDatabaseTable tableEnum)
|
private void createTable(MDatabaseTable tableEnum)
|
||||||
{
|
{
|
||||||
SQLTable table = tableEnum.getTable();
|
SQLTable table = tableEnum.getTable();
|
||||||
List<FDatabaseColumn> columns = table.getColumns();
|
List<MDatabaseColumn> columns = table.getColumns();
|
||||||
List<SQLDataType> dataTypes = table.getDataTypes();
|
List<SQLDataType> dataTypes = table.getDataTypes();
|
||||||
|
|
||||||
StringBuilder query = new StringBuilder("CREATE TABLE IF NOT EXISTS ").append(table.getTableSQLName()).append(" (");
|
StringBuilder query = new StringBuilder("CREATE TABLE IF NOT EXISTS ").append(table.getTableSQLName()).append(" (");
|
||||||
|
|
||||||
int pos = 0;
|
int pos = 0;
|
||||||
for(FDatabaseColumn column : columns)
|
for(MDatabaseColumn column : columns)
|
||||||
{
|
{
|
||||||
query.append(column.getColumn().getColumnSQLName()).append(" ").append(dataTypes.get(pos).getSQLName());
|
query.append(column.getColumn().getColumnSQLName()).append(" ").append(dataTypes.get(pos).getSQLName());
|
||||||
pos++;
|
pos++;
|
||||||
@ -91,7 +92,8 @@ public class SQLiteManager {
|
|||||||
executeUpdate(query.toString());
|
executeUpdate(query.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void executeUpdate(String query)
|
@Deprecated
|
||||||
|
boolean executeUpdate(String query)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -99,15 +101,85 @@ public class SQLiteManager {
|
|||||||
{
|
{
|
||||||
msg.critical("Lost connection to SQLite database!");
|
msg.critical("Lost connection to SQLite database!");
|
||||||
System.exit(1);
|
System.exit(1);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Statement statement = connection.createStatement();
|
Statement statement = connection.createStatement();
|
||||||
statement.executeUpdate(query);
|
statement.executeUpdate(query);
|
||||||
|
return true;
|
||||||
} catch (SQLException e)
|
} catch (SQLException e)
|
||||||
{
|
{
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
msg.critical("Error executing SQLite update!");
|
msg.critical("Error executing SQLite update!");
|
||||||
System.exit(1);
|
System.exit(1);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean executeUpdate(String query, String... strings)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
|
||||||
|
if(connection == null || connection.isClosed())
|
||||||
|
{
|
||||||
|
msg.critical("Lost connection to SQLite database!");
|
||||||
|
System.exit(1);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
PreparedStatement statement = connection.prepareStatement(query);
|
||||||
|
|
||||||
|
int pos = 1;
|
||||||
|
for(String s : strings)
|
||||||
|
{
|
||||||
|
statement.setString(pos, s);
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
statement.executeUpdate();
|
||||||
|
return true;
|
||||||
|
|
||||||
|
|
||||||
|
} catch (SQLException throwables) {
|
||||||
|
throwables.printStackTrace();
|
||||||
|
// todo: error
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> executeStatement(String query, MDatabaseColumn column, String... strings)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
if(connection == null || connection.isClosed())
|
||||||
|
{
|
||||||
|
msg.critical("Lost connection to SQLite database!");
|
||||||
|
System.exit(1);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
PreparedStatement statement = connection.prepareStatement(query);
|
||||||
|
|
||||||
|
String columnSqlName = column.getColumn().getColumnSQLName();
|
||||||
|
|
||||||
|
int pos = 1;
|
||||||
|
for(String s : strings)
|
||||||
|
{
|
||||||
|
statement.setString(pos, s);
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
|
||||||
|
ResultSet resultSet = statement.executeQuery();
|
||||||
|
List<String> values = new ArrayList<>();
|
||||||
|
while(resultSet.next())
|
||||||
|
{
|
||||||
|
values.add(resultSet.getString(columnSqlName));
|
||||||
|
}
|
||||||
|
return values;
|
||||||
|
|
||||||
|
} catch (SQLException e) {
|
||||||
|
e.printStackTrace(); //todo: error
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package net.mindoverflow.webmarker.utils.sql.primitives;
|
package net.mindoverflow.webmarker.utils.sql.primitives;
|
||||||
|
|
||||||
import net.mindoverflow.webmarker.utils.sql.FDatabaseColumn;
|
import net.mindoverflow.webmarker.utils.sql.MDatabaseColumn;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -8,22 +8,22 @@ import java.util.List;
|
|||||||
public class SQLTable
|
public class SQLTable
|
||||||
{
|
{
|
||||||
private final String tableName;
|
private final String tableName;
|
||||||
private final List<FDatabaseColumn> columns;
|
private final List<MDatabaseColumn> columns;
|
||||||
private final List<SQLDataType> columnTypes = new ArrayList<>();
|
private final List<SQLDataType> columnTypes = new ArrayList<>();
|
||||||
|
|
||||||
public SQLTable(String tableName, List<FDatabaseColumn> columns)
|
public SQLTable(String tableName, List<MDatabaseColumn> columns)
|
||||||
{
|
{
|
||||||
this.tableName = tableName;
|
this.tableName = tableName;
|
||||||
this.columns = columns;
|
this.columns = columns;
|
||||||
|
|
||||||
for(FDatabaseColumn column : columns)
|
for(MDatabaseColumn column : columns)
|
||||||
{ columnTypes.add(column.getDataType()); }
|
{ columnTypes.add(column.getDataType()); }
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getTableSQLName()
|
public String getTableSQLName()
|
||||||
{ return tableName; }
|
{ return tableName; }
|
||||||
|
|
||||||
public List<FDatabaseColumn> getColumns()
|
public List<MDatabaseColumn> getColumns()
|
||||||
{ return columns; }
|
{ return columns; }
|
||||||
|
|
||||||
public List<SQLDataType> getDataTypes()
|
public List<SQLDataType> getDataTypes()
|
||||||
|
@ -1,57 +1,19 @@
|
|||||||
package net.mindoverflow.webmarker.webserver;
|
package net.mindoverflow.webmarker.webserver;
|
||||||
|
|
||||||
import net.mindoverflow.webmarker.utils.URLMap;
|
import net.mindoverflow.webmarker.webserver.controllers.LoginController;
|
||||||
|
import net.mindoverflow.webmarker.webserver.controllers.RegisterController;
|
||||||
|
import net.mindoverflow.webmarker.webserver.controllers.StorageController;
|
||||||
import ro.pippo.controller.ControllerApplication;
|
import ro.pippo.controller.ControllerApplication;
|
||||||
import ro.pippo.core.route.TrailingSlashHandler;
|
import ro.pippo.core.route.TrailingSlashHandler;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class WebApplication extends ControllerApplication {
|
public class WebApplication extends ControllerApplication {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onInit() {
|
protected void onInit()
|
||||||
|
{
|
||||||
GET("/save", routeContext -> routeContext.send("Please append an user ID"));
|
addControllers(new LoginController());
|
||||||
GET("/save/{id}", routeContext -> routeContext.send("Please append an URL"));
|
addControllers(new RegisterController());
|
||||||
GET("/save/{id}/{url}", routeContext ->
|
addControllers(new StorageController());
|
||||||
{
|
|
||||||
int userId = routeContext.getParameter("id").toInt();
|
|
||||||
String saveUrl = routeContext.getParameter("url").toString();
|
|
||||||
|
|
||||||
URLMap.saveUrl(userId, saveUrl);
|
|
||||||
routeContext.send("Saved!");
|
|
||||||
});
|
|
||||||
|
|
||||||
GET("/get", routeContext -> routeContext.send("Please append an user ID"));
|
|
||||||
GET("/get/{id}", routeContext ->
|
|
||||||
{
|
|
||||||
int userId = routeContext.getParameter("id").toInt();
|
|
||||||
|
|
||||||
List<String> urls = URLMap.getUserUrls(userId);
|
|
||||||
StringBuilder urlsSB = new StringBuilder(" | ");
|
|
||||||
for(String url : urls)
|
|
||||||
{
|
|
||||||
urlsSB.append(url).append(" | ");
|
|
||||||
}
|
|
||||||
routeContext.send(urlsSB.toString());
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
GET("/drop", routeContext -> routeContext.send("Please append an user ID"));
|
|
||||||
GET("/drop/{id}", routeContext ->
|
|
||||||
{
|
|
||||||
int userId = routeContext.getParameter("id").toInt();
|
|
||||||
URLMap.dropUser(userId);
|
|
||||||
routeContext.send("Dropped!");
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
POST("/post", routeContext -> {
|
|
||||||
int userId = routeContext.getParameter("id").toInt();
|
|
||||||
String url = routeContext.getParameter("url").toString();
|
|
||||||
|
|
||||||
routeContext.send("AAAAAAAAAAAAA " + url);
|
|
||||||
});
|
|
||||||
|
|
||||||
ANY("/.*", new TrailingSlashHandler(false)); // remove trailing slash
|
ANY("/.*", new TrailingSlashHandler(false)); // remove trailing slash
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,82 @@
|
|||||||
|
package net.mindoverflow.webmarker.webserver.controllers;
|
||||||
|
|
||||||
|
import com.auth0.jwt.JWT;
|
||||||
|
import com.auth0.jwt.algorithms.Algorithm;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import net.mindoverflow.webmarker.utils.FileUtils;
|
||||||
|
import net.mindoverflow.webmarker.utils.config.ConfigEntries;
|
||||||
|
import net.mindoverflow.webmarker.utils.messaging.Messenger;
|
||||||
|
import net.mindoverflow.webmarker.utils.security.EncryptionUtils;
|
||||||
|
import net.mindoverflow.webmarker.utils.security.SafetyCheck;
|
||||||
|
import net.mindoverflow.webmarker.utils.sql.MarkerSQLUtils;
|
||||||
|
import ro.pippo.controller.Controller;
|
||||||
|
import ro.pippo.controller.POST;
|
||||||
|
import ro.pippo.controller.Path;
|
||||||
|
import ro.pippo.core.route.RouteContext;
|
||||||
|
|
||||||
|
import java.time.ZonedDateTime;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
@Path("/api/v1/login")
|
||||||
|
public class LoginController extends Controller
|
||||||
|
{
|
||||||
|
private final Messenger msg = new Messenger();
|
||||||
|
|
||||||
|
@POST
|
||||||
|
public void login()
|
||||||
|
{
|
||||||
|
RouteContext routeContext = getRouteContext();
|
||||||
|
|
||||||
|
|
||||||
|
String body = routeContext.getRequest().getBody();
|
||||||
|
JsonObject jsonObject = FileUtils.stringToJson(body);
|
||||||
|
|
||||||
|
String username = jsonObject.get("username").getAsString();
|
||||||
|
String encodedPassword = jsonObject.get("password").getAsString();
|
||||||
|
String encoding = jsonObject.get("encoding").getAsString();
|
||||||
|
|
||||||
|
if(!SafetyCheck.isValidEncoding(encoding))
|
||||||
|
{
|
||||||
|
routeContext.send("Invalid encoding: '" + encoding + "'!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String password = EncryptionUtils.handleEncoding(encoding, encodedPassword);
|
||||||
|
|
||||||
|
if(!SafetyCheck.isSafeUsername(username))
|
||||||
|
{
|
||||||
|
routeContext.send("Invalid username!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!SafetyCheck.isSafePassword(password))
|
||||||
|
{
|
||||||
|
routeContext.send("Invalid password!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!MarkerSQLUtils.userExists(username))
|
||||||
|
{
|
||||||
|
routeContext.send("User does not exist!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String bcryptedStoredPassword = MarkerSQLUtils.getUserBcryptedPassword(username);
|
||||||
|
|
||||||
|
if(!EncryptionUtils.bcryptMatches(bcryptedStoredPassword, password))
|
||||||
|
{
|
||||||
|
routeContext.send("Wrong password!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// JWT
|
||||||
|
Algorithm algorithm = Algorithm.HMAC256((String) ConfigEntries.JWT_SECRET.getValue());
|
||||||
|
String token = JWT.create()
|
||||||
|
.withClaim("username", username)
|
||||||
|
.withExpiresAt(Date.from(ZonedDateTime.now().plusMinutes(60).toInstant()))
|
||||||
|
.sign(algorithm);
|
||||||
|
|
||||||
|
routeContext.send(token);
|
||||||
|
msg.info("User " + username + " logged in!");
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,68 @@
|
|||||||
|
package net.mindoverflow.webmarker.webserver.controllers;
|
||||||
|
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import net.mindoverflow.webmarker.utils.FileUtils;
|
||||||
|
import net.mindoverflow.webmarker.utils.security.EncryptionUtils;
|
||||||
|
import net.mindoverflow.webmarker.utils.security.SafetyCheck;
|
||||||
|
import net.mindoverflow.webmarker.utils.sql.MarkerSQLUtils;
|
||||||
|
import ro.pippo.controller.Controller;
|
||||||
|
import ro.pippo.controller.POST;
|
||||||
|
import ro.pippo.controller.Path;
|
||||||
|
import ro.pippo.core.route.RouteContext;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@Path("/api/v1/register")
|
||||||
|
public class RegisterController extends Controller
|
||||||
|
{
|
||||||
|
@POST
|
||||||
|
public void register()
|
||||||
|
{
|
||||||
|
RouteContext routeContext = getRouteContext();
|
||||||
|
|
||||||
|
String body = routeContext.getRequest().getBody();
|
||||||
|
JsonObject jsonObject = FileUtils.stringToJson(body);
|
||||||
|
|
||||||
|
String username = jsonObject.get("username").getAsString();
|
||||||
|
String encodedPassword = jsonObject.get("password").getAsString();
|
||||||
|
String encoding = jsonObject.get("encoding").getAsString();
|
||||||
|
|
||||||
|
if(!SafetyCheck.isValidEncoding(encoding))
|
||||||
|
{
|
||||||
|
routeContext.send("Invalid encoding: '" + encoding + "'!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String password = EncryptionUtils.handleEncoding(encoding, encodedPassword);
|
||||||
|
|
||||||
|
if(!SafetyCheck.isSafeUsername(username))
|
||||||
|
{
|
||||||
|
routeContext.send("Invalid username!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!SafetyCheck.isSafePassword(password))
|
||||||
|
{
|
||||||
|
routeContext.send("Invalid password!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(MarkerSQLUtils.userExists(username))
|
||||||
|
{
|
||||||
|
routeContext.send("User exists!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// generate a random UUID, to identify same user in different tables
|
||||||
|
UUID randomId = UUID.randomUUID();
|
||||||
|
|
||||||
|
// check if the UUID is already taken by another user
|
||||||
|
while(MarkerSQLUtils.uuidTaken(randomId))
|
||||||
|
{
|
||||||
|
randomId = UUID.randomUUID();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(MarkerSQLUtils.addUser(randomId, username, EncryptionUtils.bcrypt(password))) routeContext.send("Added user!");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,162 @@
|
|||||||
|
package net.mindoverflow.webmarker.webserver.controllers;
|
||||||
|
|
||||||
|
import com.auth0.jwt.JWT;
|
||||||
|
import com.auth0.jwt.JWTVerifier;
|
||||||
|
import com.auth0.jwt.algorithms.Algorithm;
|
||||||
|
import com.auth0.jwt.exceptions.JWTVerificationException;
|
||||||
|
import com.auth0.jwt.interfaces.Claim;
|
||||||
|
import com.auth0.jwt.interfaces.DecodedJWT;
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import net.mindoverflow.webmarker.utils.FileUtils;
|
||||||
|
import net.mindoverflow.webmarker.utils.config.ConfigEntries;
|
||||||
|
import net.mindoverflow.webmarker.utils.security.SafetyCheck;
|
||||||
|
import net.mindoverflow.webmarker.utils.sql.MarkerSQLUtils;
|
||||||
|
import ro.pippo.controller.Controller;
|
||||||
|
import ro.pippo.controller.POST;
|
||||||
|
import ro.pippo.controller.Path;
|
||||||
|
import ro.pippo.core.route.RouteContext;
|
||||||
|
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@Path("/api/v1/store")
|
||||||
|
public class StorageController extends Controller
|
||||||
|
{
|
||||||
|
|
||||||
|
private RouteContext routeContext;
|
||||||
|
|
||||||
|
@POST
|
||||||
|
public void store()
|
||||||
|
{
|
||||||
|
// Load RouteContext
|
||||||
|
routeContext = getRouteContext();
|
||||||
|
|
||||||
|
// Load JSON object from body
|
||||||
|
String body = routeContext.getRequest().getBody();
|
||||||
|
JsonObject jsonObject = FileUtils.stringToJson(body);
|
||||||
|
|
||||||
|
// Load JWT element
|
||||||
|
JsonElement jwtElement = getJwtFromJson(jsonObject);
|
||||||
|
if(jwtElement == null) return;
|
||||||
|
|
||||||
|
// Decrypt/Verify JWT
|
||||||
|
DecodedJWT jwt = decryptAndVerifyJwt(jwtElement);
|
||||||
|
if(jwt == null) return;
|
||||||
|
|
||||||
|
// Load username from JWT
|
||||||
|
String username = getFromJwt(jwt, "username");
|
||||||
|
if(username == null) return;
|
||||||
|
|
||||||
|
// Check some stuff about the user
|
||||||
|
if(!userCheckPassed(username)) return;
|
||||||
|
|
||||||
|
// Load UUID from username
|
||||||
|
UUID uuid = MarkerSQLUtils.getUserUUID(username);
|
||||||
|
if(uuid == null)
|
||||||
|
{
|
||||||
|
routeContext.send("Server error: missing UUID!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load url from Json body
|
||||||
|
String url = getFromJson(jsonObject, "url");
|
||||||
|
if(url == null) return;
|
||||||
|
|
||||||
|
// Verify url validity
|
||||||
|
URI confirmedUrl = verifyUrl(url);
|
||||||
|
if(confirmedUrl == null) return;
|
||||||
|
|
||||||
|
MarkerSQLUtils.addHistoryRecord(uuid, confirmedUrl.toString());
|
||||||
|
routeContext.send("OK!");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private JsonElement getJwtFromJson(JsonObject jsonObject)
|
||||||
|
{
|
||||||
|
JsonElement jwtElement = jsonObject.get("jwt");
|
||||||
|
if(jwtElement == null || jwtElement.isJsonNull())
|
||||||
|
{
|
||||||
|
routeContext.send("Invalid JWT!"); //todo: throw exception instead?
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return jwtElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
private DecodedJWT decryptAndVerifyJwt(JsonElement jwtElement)
|
||||||
|
{
|
||||||
|
String token = jwtElement.getAsString();
|
||||||
|
|
||||||
|
try {
|
||||||
|
Algorithm algorithm = Algorithm.HMAC256((String) ConfigEntries.JWT_SECRET.getValue());
|
||||||
|
JWTVerifier jwtVerifier = JWT.require(algorithm)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
return jwtVerifier.verify(token);
|
||||||
|
}
|
||||||
|
catch (JWTVerificationException e)
|
||||||
|
{
|
||||||
|
routeContext.send("Invalid JWT!");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getFromJwt(DecodedJWT jwt, String claimName)
|
||||||
|
{
|
||||||
|
Claim claim = jwt.getClaim(claimName);
|
||||||
|
if(claim == null || claim.isNull())
|
||||||
|
{
|
||||||
|
routeContext.send("JWT missing '" + claimName + "' claim!");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return claim.asString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean userCheckPassed(String username)
|
||||||
|
{
|
||||||
|
if(!SafetyCheck.isSafeUsername(username))
|
||||||
|
{
|
||||||
|
routeContext.send("Invalid username!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!MarkerSQLUtils.userExists(username))
|
||||||
|
{
|
||||||
|
routeContext.send("User does not exist!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getFromJson(JsonObject jsonObject, String name)
|
||||||
|
{
|
||||||
|
|
||||||
|
JsonElement jsonElement = jsonObject.get(name);
|
||||||
|
if(jsonElement == null || jsonElement.isJsonNull())
|
||||||
|
{
|
||||||
|
routeContext.send("JSON body missing '" + name + "' entry!");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return jsonElement.getAsString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private URI verifyUrl(String url)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
return new URL(url).toURI();
|
||||||
|
} catch (URISyntaxException e) {
|
||||||
|
routeContext.send("Invalid URI!");
|
||||||
|
}
|
||||||
|
catch (MalformedURLException e) {
|
||||||
|
routeContext.send("Invalid URL!");
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
@ -1 +1,2 @@
|
|||||||
port: 7344
|
port: 7344
|
||||||
|
secret: '398JC3lDk1Ckaock3dcnc938COAk9d'
|
Loading…
Reference in New Issue
Block a user