Added user registration and login | u_reg and a_lru

This commit is contained in:
Julia Butenhoff 2022-07-13 21:21:40 -05:00
parent 4d49a2755b
commit 1db3d51796
11 changed files with 476 additions and 25 deletions

View File

@ -1,30 +1,106 @@
package com.icedberries.UBFunkeysServer.ArkOne;
import com.icedberries.UBFunkeysServer.ArkOne.Plugins.BasePlugin;
import com.icedberries.UBFunkeysServer.ArkOne.Plugins.GalaxyPlugin;
import com.icedberries.UBFunkeysServer.ArkOne.Plugins.UserPlugin;
import javagrinko.spring.tcp.Connection;
import javagrinko.spring.tcp.TcpController;
import javagrinko.spring.tcp.TcpHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.w3c.dom.Element;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@TcpController
public class ArkOneController implements TcpHandler {
public static final String IP_ADDRESS = "127.0.0.1";
// Plugins
@Autowired
BasePlugin basePlugin;
@Autowired
UserPlugin userPlugin;
@Autowired
GalaxyPlugin galaxyPlugin;
@Override
public void receiveData(Connection connection, byte[] data) {
//TODO: IMPLEMENT PLUGIN CHECKING AND FORWARDING HERE
//Currently just echos back the received data until implemented
// Log the received request
String xmlData = new String(data);
System.out.println("[ArkOne] New Request: " + xmlData);
//TODO: USE THAT FOR PASSWORD ENCRYPTION FOR STORING IN THE DB
//TODO: SEE THIS ARTICLE FOR ENCODING: https://www.baeldung.com/spring-security-registration-password-encoding-bcrypt
// Create a list of responses to send back
ArrayList<String> responses = new ArrayList<>();
try {
connection.send(xmlData.toUpperCase().getBytes());
} catch (IOException e) {
e.printStackTrace();
// Parse the incoming data into individual commands
List<String> commands = ArkOneParser.ParseReceivedMessage(xmlData);
// Handle each command
for (String command : commands) {
try {
Element commandInfo = (Element)ArkOneParser.ParseCommand(command);
switch(commandInfo.getNodeName()) {
// Plugin 0 - Core
case "a_lgu":
responses.add(basePlugin.LoginGuestUser());
break;
case "a_gpd":
responses.add(basePlugin.GetPluginDetails(commandInfo.getAttribute("p")));
break;
case "a_gsd":
responses.add(basePlugin.GetServiceDetails(commandInfo.getAttribute("s")));
break;
case "a_lru":
responses.add(basePlugin.LoginRegisteredUser(commandInfo));
break;
// Plugin 1 (User)
case "u_reg":
responses.add(userPlugin.RegisterUser(commandInfo));
break;
// Plugin 7 (Galaxy)
case "lpv":
responses.add(galaxyPlugin.LoadProfileVersion());
break;
// Plugin 10 (Trunk)
// Catch Unhandled Commands
default:
responses.add("<unknown />");
System.out.println("[ArkOne][ERROR] Unhandled command: " + commandInfo.getNodeName());
break;
}
} catch (Exception e) {
System.out.println("[ArkOne][ERROR] Unknown error occurred: ");
e.printStackTrace();
responses.add("<unknown />");
}
}
// Send the response
for(String response : responses) {
try {
// Append a 0x00 to the end of the response
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
outputStream.write(response.getBytes());
outputStream.write((byte)0x00);
byte[] combinedResponse = outputStream.toByteArray();
connection.send(combinedResponse);
System.out.println("[ArkOne] Response: " + response);
} catch (IOException e) {
System.out.println("[ArkOne][ERROR] Unknown error occurred: ");
e.printStackTrace();
}
}
}

View File

@ -0,0 +1,71 @@
package com.icedberries.UBFunkeysServer.ArkOne;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.xml.sax.InputSource;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class ArkOneParser {
public static ArrayList<String> ParseReceivedMessage(String xmlCommand) {
List<String> rawCommandsList = Arrays.stream(xmlCommand.split("\0"))
.filter(str -> !isNullOrWhitespace(str))
.collect(Collectors.toList());
return new ArrayList<>(rawCommandsList);
}
public static Node ParseCommand(String command) throws Exception {
// Check to see if the command has a routing string at the end of it
if (command.endsWith("#")) {
command = command.substring(0, command.lastIndexOf(">") + 1);
}
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
Document doc = dBuilder.parse(new InputSource(new StringReader(command)));
doc.getDocumentElement().normalize();
return doc.getFirstChild();
}
public static String RemoveXMLTag(Document doc) throws TransformerException {
DOMSource domSource = new DOMSource(doc);
StringWriter writer = new StringWriter();
StreamResult result = new StreamResult(writer);
TransformerFactory tf = TransformerFactory.newInstance();
Transformer transformer = tf.newTransformer();
transformer.transform(domSource, result);
return writer.toString().replaceAll("(<\\?xml.*?\\?>)","");
}
public static boolean isNullOrWhitespace(String s) {
return s == null || isWhitespace(s);
}
private static boolean isWhitespace(String s) {
int length = s.length();
if (length > 0) {
for (int i = 0; i < length; i++) {
if (!Character.isWhitespace(s.charAt(i))) {
return false;
}
}
return true;
}
return false;
}
}

View File

@ -0,0 +1,206 @@
package com.icedberries.UBFunkeysServer.ArkOne.Plugins;
import com.icedberries.UBFunkeysServer.ArkOne.ArkOneController;
import com.icedberries.UBFunkeysServer.ArkOne.ArkOneParser;
import com.icedberries.UBFunkeysServer.domain.User;
import com.icedberries.UBFunkeysServer.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;
@Service
public class BasePlugin {
@Autowired
private UserService userService;
@Autowired
private PasswordEncoder passwordEncoder;
private static final String PORT = "20502";
public String LoginGuestUser() throws ParserConfigurationException, TransformerException {
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
// Create the root element
Document doc = dBuilder.newDocument();
Element rootElement = doc.createElement("a_lgu");
// Set attributes
rootElement.setAttribute("r", "0");
rootElement.setAttribute("u", "0");
rootElement.setAttribute("n", "GUESTUSER");
rootElement.setAttribute("p", "");
rootElement.setAttribute("s", "1");
doc.appendChild(rootElement);
return ArkOneParser.RemoveXMLTag(doc);
}
public String GetServiceDetails(String s) throws ParserConfigurationException, TransformerException {
// Set some variables for the response
String serverID = "1";
String xIPAddress = ArkOneController.IP_ADDRESS;
String xPort = "80";
String bIPAddress = ArkOneController.IP_ADDRESS;
String bPort = "80";
// Update the port for each server
// ** This would be used if each routing plugin had their own server/port combo **
switch (s){
//User
case "1":
xPort = PORT;
bPort = PORT;
break;
//Galaxy
case "7":
xPort = PORT;
bPort = PORT;
break;
//Trunk
case "10":
xPort = PORT;
bPort = PORT;
break;
}
// Build the response
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
// Create the root element
Document doc = dBuilder.newDocument();
Element rootElement = doc.createElement("a_gsd");
// Set attributes
rootElement.setAttribute("s", serverID);
rootElement.setAttribute("xi", xIPAddress);
rootElement.setAttribute("xp", xPort);
rootElement.setAttribute("bi", bIPAddress);
rootElement.setAttribute("bp", bPort);
doc.appendChild(rootElement);
return ArkOneParser.RemoveXMLTag(doc);
}
public String GetPluginDetails(String p) throws ParserConfigurationException, TransformerException {
// Set some variables for the response
String serverID = "1";
String xIPAddress = ArkOneController.IP_ADDRESS;
String xPort = "80";
String bIPAddress = ArkOneController.IP_ADDRESS;
String bPort = "80";
// Update the port for each plugin
// ** This would be used if each routing plugin had their own server/port combo **
switch (p){
//User
case "1":
xPort = PORT;
bPort = PORT;
break;
//Galaxy
case "7":
xPort = PORT;
bPort = PORT;
break;
//Trunk
case "10":
xPort = PORT;
bPort = PORT;
break;
}
// Build the response
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
// Create the root element
Document doc = dBuilder.newDocument();
Element rootElement = doc.createElement("a_gpd");
// Set attributes
rootElement.setAttribute("s", serverID);
rootElement.setAttribute("xi", xIPAddress);
rootElement.setAttribute("xp", xPort);
rootElement.setAttribute("bi", bIPAddress);
rootElement.setAttribute("bp", bPort);
rootElement.setAttribute("p", p);
doc.appendChild(rootElement);
return ArkOneParser.RemoveXMLTag(doc);
}
public String LoginRegisteredUser(Element element) throws ParserConfigurationException, TransformerException {
// Response r codes:
// 0 - Accepted Login
// 1 - Already exist in Terrapinia
// 2 - Already exist in Terrapinia
// 3 - Problem with your account, please call phone number
// 4 - Password Incorrect
// 5 - Funkey Name Not Found
Integer responseCode = 0;
String username = element.getAttribute("n");
String password = element.getAttribute("p");
// Get a funkey with that username
User user = userService.findByUsername(username).orElse(null);
String uuid = "";
// Check if null (no funkey with that name)
if (user == null) {
responseCode = 5;
} else {
// Funkey name exists - Verify Password
if (!passwordEncoder.matches(password, user.getPassword())) {
// Passwords don't match
responseCode = 4;
} else {
// Login success
uuid = String.valueOf(user.getUUID());
}
}
// Build response
// Build the response
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
// Create the root element
Document doc = dBuilder.newDocument();
Element rootElement = doc.createElement("a_lru");
// Set attributes
rootElement.setAttribute("s", "1");
rootElement.setAttribute("r", String.valueOf(responseCode));
if (responseCode == 0) {
// Only set the user id field if successfully found the user
rootElement.setAttribute("u", uuid);
}
doc.appendChild(rootElement);
return ArkOneParser.RemoveXMLTag(doc);
}
}

View File

@ -0,0 +1,12 @@
package com.icedberries.UBFunkeysServer.ArkOne.Plugins;
import org.springframework.stereotype.Service;
@Service
public class GalaxyPlugin {
public String LoadProfileVersion() {
//TODO: IMPLEMENT THIS WITH PROFILE SAVING
return "<h7_0><lpv /></h7_0>";
}
}

View File

@ -0,0 +1,73 @@
package com.icedberries.UBFunkeysServer.ArkOne.Plugins;
import com.icedberries.UBFunkeysServer.ArkOne.ArkOneParser;
import com.icedberries.UBFunkeysServer.domain.User;
import com.icedberries.UBFunkeysServer.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;
@Service
public class UserPlugin {
@Autowired
private PasswordEncoder passwordEncoder;
@Autowired
private UserService userService;
public String RegisterUser(Element element) throws ParserConfigurationException, TransformerException {
String username = element.getAttribute("l");
String password = element.getAttribute("p");
String securityQuestion = element.getAttribute("sq");
String securityAnswer = element.getAttribute("sa");
User newUser = User.builder()
.username(username)
.password(passwordEncoder.encode(password))
.securityQuestion(securityQuestion)
.securityAnswer(securityAnswer)
.build();
// 0 - Successfully registered
// 1 - Name already exists
// 2 - Issues connecting to server
Integer responseCode = 0;
String uniqueId = "";
// First check if username doesn't already exist in the DB
if (userService.existsByUsername(newUser.getUsername())) {
// Username already exists
responseCode = 1;
} else {
// Username doesn't exist - save it
User newUserInDB = userService.save(newUser);
uniqueId = String.valueOf(newUserInDB.getUUID());
}
// Build response
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
// Create the root element
Document doc = dBuilder.newDocument();
Element rootElement = doc.createElement("u_reg");
// Set attributes
rootElement.setAttribute("r", String.valueOf(responseCode));
rootElement.setAttribute("u", uniqueId);
doc.appendChild(rootElement);
return ArkOneParser.RemoveXMLTag(doc);
}
}

View File

@ -1,5 +1,6 @@
package com.icedberries.UBFunkeysServer.Galaxy;
import com.icedberries.UBFunkeysServer.ArkOne.ArkOneParser;
import com.icedberries.UBFunkeysServer.domain.Crib;
import com.icedberries.UBFunkeysServer.service.CribService;
import com.icedberries.UBFunkeysServer.service.EmailService;
@ -245,13 +246,7 @@ public class GalaxyServer {
Node importedNode = newDocument.importNode(profileNode, true);
newDocument.appendChild(importedNode);
DOMSource domSource = new DOMSource(newDocument);
StringWriter writer = new StringWriter();
StreamResult result = new StreamResult(writer);
TransformerFactory tf = TransformerFactory.newInstance();
Transformer transformer = tf.newTransformer();
transformer.transform(domSource, result);
profileData = writer.toString().replaceAll("(<\\?xml.*?\\?>)","");
profileData = ArkOneParser.RemoveXMLTag(newDocument);
} catch (ParserConfigurationException | TransformerException e) {
System.out.println("[Galaxy][POST] Exception thrown when saving crib: ");
e.printStackTrace();

View File

@ -5,12 +5,9 @@ import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.hibernate.annotations.Type;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.*;
@Getter
@Setter
@ -29,5 +26,7 @@ public class Crib {
private String username;
@Column(columnDefinition = "MEDIUMTEXT")
@Type(type = "org.hibernate.type.TextType")
private String profileData;
}

View File

@ -15,4 +15,9 @@ public interface UserRepository extends CrudRepository<User, Integer> {
@Query("select user from User user where user.UUID = :uuid")
Optional<User> findByUUID(@Param("uuid") Integer uuid);
Boolean existsByUsername(String username);
@Query("select user from User user where user.username = :username")
Optional<User> findByUsername(@Param("username") String username);
}

View File

@ -10,5 +10,9 @@ public interface UserService {
Boolean existsByUUID(Integer uuid);
void save(User user);
User save(User user);
Boolean existsByUsername(String username);
Optional<User> findByUsername(String username);
}

View File

@ -26,7 +26,17 @@ public class UserServiceImpl implements UserService {
}
@Override
public void save(User user) {
userRepository.save(user);
public User save(User user) {
return userRepository.save(user);
}
@Override
public Boolean existsByUsername(String username) {
return userRepository.existsByUsername(username);
}
@Override
public Optional<User> findByUsername(String username) {
return userRepository.findByUsername(username);
}
}

View File

@ -57,7 +57,7 @@ public class TcpConnection implements Connection {
public void start() {
new Thread(() -> {
while (true) {
byte buf[] = new byte[64 * 1024];
byte[] buf = new byte[51200];
try {
int count = inputStream.read(buf);
if (count > 0) {