Added Register Workflow Request Handler and fixed some bugs

This commit is contained in:
CodingPhoenixx
2026-02-15 21:32:06 +01:00
parent 6eca824961
commit ffbf4f7500
11 changed files with 280 additions and 12 deletions
@@ -3,6 +3,7 @@ package dev.coph.flightscore.backend;
import dev.coph.flightscore.backend.config.Config; import dev.coph.flightscore.backend.config.Config;
import dev.coph.flightscore.backend.provider.ProviderManager; import dev.coph.flightscore.backend.provider.ProviderManager;
import dev.coph.flightscore.backend.requestHandler.auth.LoginRequestHandler; import dev.coph.flightscore.backend.requestHandler.auth.LoginRequestHandler;
import dev.coph.flightscore.backend.requestHandler.auth.RegisterRequestHandler;
import dev.coph.flightscore.backend.user.UserProvider; import dev.coph.flightscore.backend.user.UserProvider;
import dev.coph.flightscore.backend.user.permission.PermissionProvider; import dev.coph.flightscore.backend.user.permission.PermissionProvider;
import dev.coph.flightscore.backend.user.role.RoleProvider; import dev.coph.flightscore.backend.user.role.RoleProvider;
@@ -78,6 +79,7 @@ public class Backend {
logger.info("Loading routes..."); logger.info("Loading routes...");
webServer.registerRequestHandler(new LoginRequestHandler(this)); webServer.registerRequestHandler(new LoginRequestHandler(this));
webServer.registerRequestHandler(new RegisterRequestHandler(this));
logger.success("Routes loaded!"); logger.success("Routes loaded!");
@@ -0,0 +1,25 @@
package dev.coph.flightscore.backend.country;
import java.util.*;
import java.util.stream.Collectors;
public final class CountryRepository {
private static final Map<String, Locale> byCode = Arrays.stream(Locale.getISOCountries())
.map(code -> new Locale("", code))
.collect(Collectors.toMap(locale -> locale.getCountry().toLowerCase(), locale -> locale));
private static final Map<String, Locale> byName = byCode.values().stream()
.collect(Collectors.toMap(locale -> locale.getDisplayCountry(Locale.ENGLISH).toLowerCase(), locale -> locale));
public static Locale fromCode(String code) {
return byCode.get(code.toLowerCase());
}
public static Locale fromName(String name) {
return byName.get(name.toLowerCase());
}
public static List<Locale> all() {
return new ArrayList<>(byCode.values());
}
}
@@ -47,7 +47,7 @@ public class LoginRequestHandler {
if (email == null || password == null) { if (email == null || password == null) {
response.setStatus(HttpStatus.BAD_REQUEST_400); response.setStatus(HttpStatus.BAD_REQUEST_400);
ResponseUtil.writeAnswer(response, callback, "Missing email or password"); ResponseUtil.writeAnswer(response, callback, "Invalid body");
return; return;
} }
var loginResponse = backend.userProvider().login(email, password); var loginResponse = backend.userProvider().login(email, password);
@@ -0,0 +1,81 @@
package dev.coph.flightscore.backend.requestHandler.auth;
import dev.coph.flightscore.backend.Backend;
import dev.coph.flightscore.backend.country.CountryRepository;
import dev.coph.simplelogger.Logger;
import dev.coph.simplerequest.body.Body;
import dev.coph.simplerequest.body.JsonBody;
import dev.coph.simplerequest.handler.RequestHandler;
import dev.coph.simplerequest.handler.RequestMethod;
import dev.coph.simplerequest.util.ResponseUtil;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.server.Response;
import org.eclipse.jetty.util.Callback;
import org.json.JSONObject;
import java.io.IOException;
import java.util.Locale;
public class RegisterRequestHandler {
private final Logger logger = Logger.of("RH - Register");
private final Backend backend;
public RegisterRequestHandler(Backend backend) {
this.backend = backend;
}
@RequestHandler(
path = "/auth/register",
method = RequestMethod.POST
)
public void handle(Response response, Callback callback, Body body) {
JsonBody json;
try {
json = body.asJSON();
} catch (IOException e) {
logger.error("Failed to parse JSON body", e);
response.setStatus(HttpStatus.BAD_REQUEST_400);
ResponseUtil.writeAnswer(response, callback, "Invalid body");
return;
}
String email = json.getString("email", response, callback, "Invalid body");
if (email == null) return;
String fistName = json.getString("firstName", response, callback, "Invalid body");
if (fistName == null) return;
String lastName = json.getString("lastName", response, callback, "Invalid body");
if (lastName == null) return;
String phoneNumber = json.getString("phoneNumber", response, callback, "Invalid body");
if (phoneNumber == null) return;
String countryString = json.getString("country", response, callback, "Invalid body");
if (countryString == null) return;
String password = json.getString("password", response, callback, "Invalid body");
if (password == null) return;
if (backend.userProvider().userExists(email)) {
logger.warn("User with email " + email + " already exists");
response.setStatus(HttpStatus.CONFLICT_409);
ResponseUtil.writeAnswer(response, callback, "User already exists");
return;
}
Locale country = CountryRepository.fromCode(countryString);
var loginResponse = backend.userProvider().register(fistName, lastName, email, phoneNumber, password, country);
if (!loginResponse.success()) {
ResponseUtil.writeAnswer(response, callback, loginResponse.message());
return;
}
var responseObject = new JSONObject();
responseObject.put("accessToken", loginResponse.accessToken());
responseObject.put("refreshToken", loginResponse.refreshToken());
ResponseUtil.writeSuccessfulAnswer(response, callback, responseObject);
}
}
@@ -5,6 +5,8 @@ import dev.coph.simpleutilities.ulid.ULID;
import lombok.Getter; import lombok.Getter;
import lombok.experimental.Accessors; import lombok.experimental.Accessors;
import java.util.Locale;
@Getter @Getter
@Accessors(fluent = true) @Accessors(fluent = true)
public class User { public class User {
@@ -14,25 +16,28 @@ public class User {
private String lastName; private String lastName;
private String email; private String email;
private String phoneNumber; private String phoneNumber;
private Locale country;
private Role role; private Role role;
private boolean blocked; private boolean blocked;
public User(ULID id, String firstname, String lastname, String email, String phoneNumber, boolean blocked) { public User(ULID id, String firstname, String lastname, String email, String phoneNumber, Locale country, boolean blocked) {
this.id = id; this.id = id;
this.firstName = firstname; this.firstName = firstname;
this.lastName = lastname; this.lastName = lastname;
this.email = email; this.email = email;
this.phoneNumber = phoneNumber; this.phoneNumber = phoneNumber;
this.country = country;
this.blocked = blocked; this.blocked = blocked;
} }
public User(ULID id, String firstname, String lastname, String email, String phoneNumber, boolean blocked, Role role) { public User(ULID id, String firstname, String lastname, String email, String phoneNumber, Locale country, boolean blocked, Role role) {
this.id = id; this.id = id;
this.firstName = firstname; this.firstName = firstname;
this.lastName = lastname; this.lastName = lastname;
this.email = email; this.email = email;
this.phoneNumber = phoneNumber; this.phoneNumber = phoneNumber;
this.role = role; this.role = role;
this.country = country;
this.blocked = blocked; this.blocked = blocked;
} }
} }
@@ -3,6 +3,7 @@ package dev.coph.flightscore.backend.user;
import dev.coph.flightscore.backend.Backend; import dev.coph.flightscore.backend.Backend;
import dev.coph.flightscore.backend.actionResult.LoginActionResult; import dev.coph.flightscore.backend.actionResult.LoginActionResult;
import dev.coph.flightscore.backend.config.Config; import dev.coph.flightscore.backend.config.Config;
import dev.coph.flightscore.backend.country.CountryRepository;
import dev.coph.flightscore.backend.provider.Provider; import dev.coph.flightscore.backend.provider.Provider;
import dev.coph.flightscore.backend.user.role.Role; import dev.coph.flightscore.backend.user.role.Role;
import dev.coph.flightscore.backend.utils.Hash; import dev.coph.flightscore.backend.utils.Hash;
@@ -20,11 +21,13 @@ import dev.coph.simplesql.database.attributes.DataType;
import dev.coph.simplesql.query.Query; import dev.coph.simplesql.query.Query;
import dev.coph.simpleutilities.ulid.ULID; import dev.coph.simpleutilities.ulid.ULID;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.json.JSONArray;
import java.sql.Timestamp; import java.sql.Timestamp;
import java.time.Duration; import java.time.Duration;
import java.time.Instant; import java.time.Instant;
import java.util.List; import java.util.List;
import java.util.Locale;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
@@ -62,6 +65,7 @@ public class UserProvider implements Provider {
.column("lastname", DataType.VARCHAR, 255) .column("lastname", DataType.VARCHAR, 255)
.column("email", DataType.VARCHAR, 255, ColumnType.UNIQUE, true) .column("email", DataType.VARCHAR, 255, ColumnType.UNIQUE, true)
.column("phoneNumber", DataType.VARCHAR, 255) .column("phoneNumber", DataType.VARCHAR, 255)
.column("country", DataType.CHAR, 2)
.column("password", DataType.VARCHAR, 255, true) .column("password", DataType.VARCHAR, 255, true)
.column(new Column("blocked", DataType.BOOLEAN, true).defaultValue(false)) .column(new Column("blocked", DataType.BOOLEAN, true).defaultValue(false))
.column("role", DataType.BINARY, 26) .column("role", DataType.BINARY, 26)
@@ -127,6 +131,7 @@ public class UserProvider implements Provider {
resultSet.getString("lastname"), resultSet.getString("lastname"),
resultSet.getString("email"), resultSet.getString("email"),
resultSet.getString("phoneNumber"), resultSet.getString("phoneNumber"),
CountryRepository.fromCode(resultSet.getString("country")),
resultSet.getBoolean("blocked"), resultSet.getBoolean("blocked"),
role role
); );
@@ -138,6 +143,7 @@ public class UserProvider implements Provider {
resultSet.getString("lastname"), resultSet.getString("lastname"),
resultSet.getString("email"), resultSet.getString("email"),
resultSet.getString("phoneNumber"), resultSet.getString("phoneNumber"),
CountryRepository.fromCode(resultSet.getString("country")),
resultSet.getBoolean("blocked") resultSet.getBoolean("blocked")
); );
userReference.set(user); userReference.set(user);
@@ -197,6 +203,7 @@ public class UserProvider implements Provider {
resultSet.getString("lastname"), resultSet.getString("lastname"),
resultSet.getString("email"), resultSet.getString("email"),
resultSet.getString("phoneNumber"), resultSet.getString("phoneNumber"),
CountryRepository.fromCode(resultSet.getString("country")),
resultSet.getBoolean("blocked"), resultSet.getBoolean("blocked"),
role role
); );
@@ -208,6 +215,7 @@ public class UserProvider implements Provider {
resultSet.getString("lastname"), resultSet.getString("lastname"),
resultSet.getString("email"), resultSet.getString("email"),
resultSet.getString("phoneNumber"), resultSet.getString("phoneNumber"),
CountryRepository.fromCode(resultSet.getString("country")),
resultSet.getBoolean("blocked") resultSet.getBoolean("blocked")
); );
userReference.set(user); userReference.set(user);
@@ -298,7 +306,7 @@ public class UserProvider implements Provider {
return new LoginActionResult(user, accessToken, refreshToken); return new LoginActionResult(user, accessToken, refreshToken);
} }
public LoginActionResult register(String firstName, String lastName, String email, String phoneNumber, String password) { public LoginActionResult register(String firstName, String lastName, String email, String phoneNumber, String password, Locale country) {
Role role = backend.roleProvider().defaultRole(); Role role = backend.roleProvider().defaultRole();
var userId = ULID.randomUlid(); var userId = ULID.randomUlid();
@@ -313,10 +321,15 @@ public class UserProvider implements Provider {
.entry("email", email) .entry("email", email)
.entry("phoneNumber", phoneNumber) .entry("phoneNumber", phoneNumber)
.entry("password", hashedPassword) .entry("password", hashedPassword)
.entry("role", role.id().toString()); .entry("country", country.getCountry());
if (role != null)
insert.entry("role", role.id().toString());
query.executeQuery(insert); query.executeQuery(insert);
User user = new User(userId, firstName, lastName, email, phoneNumber, false, role); User user = new User(userId, firstName, lastName, email, phoneNumber, country, false, role);
userCache.put(user.id(), user); userCache.put(user.id(), user);
long expiresAt = System.currentTimeMillis() + JWT_EXPIRATION_TIME; long expiresAt = System.currentTimeMillis() + JWT_EXPIRATION_TIME;
@@ -336,17 +349,36 @@ public class UserProvider implements Provider {
return new LoginActionResult(user, accessToken, refreshToken); return new LoginActionResult(user, accessToken, refreshToken);
} }
public boolean userExists(String email) {
Query query = new Query(backend.databaseAdapter());
AtomicBoolean exists = new AtomicBoolean(false);
var select = Query.select()
.table("users")
.condition("email", email)
.columnKey("id")
.resultActionAfterQuery(srs -> {
srs.next(_ -> exists.set(true), () -> exists.set(false), e -> logger.error("Error checking if user exists", e));
});
query.executeQuery(select);
return exists.get();
}
private String createAccessToken(User user, long expiresAt) { private String createAccessToken(User user, long expiresAt) {
try { try {
return new JWT.Builder() JWT.Builder builder = new JWT.Builder()
.audience("flightscore-api") .audience("flightscore-api")
.issuer("flightscore-api") .issuer("flightscore-api")
.subject(user.email()) .subject(user.id().toString())
.claim("id", user.id().toString())
.claim("role", user.role().id().toString())
.expiresAt(expiresAt / 1000) .expiresAt(expiresAt / 1000)
.issuedAt(System.currentTimeMillis() / 1000) .issuedAt(System.currentTimeMillis() / 1000);
.sign(JWT_SECRET);
if (user.role() != null) {
JSONArray permissions = new JSONArray();
user.role().permissions().forEach(permission -> permissions.put(permission.name()));
builder.claim("permission", permissions.toString());
builder.claim("role", user.role().id().toString());
}
return builder.sign(JWT_SECRET);
} catch (JwtException e) { } catch (JwtException e) {
logger.error("Error creating JWT", e); logger.error("Error creating JWT", e);
} }
+38
View File
@@ -0,0 +1,38 @@
type: environment
model: environment
id: ev_rnW4WEytTs
workspaceId: wk_gViMDNsLC9
createdAt: 2025-07-25T17:21:10.849676300
updatedAt: 2026-02-15T19:39:14.624560900
name: Global Variables
public: true
base: true
parentModel: workspace
parentId: null
variables:
- enabled: true
name: BASE_URL
value: http://127.0.0.1:8080
id: 9FaWWJII9c
- enabled: true
name: email
value: jan.meinl@coph.dev
id: 5g83mX2k0a
- enabled: true
name: firstname
value: Jan
id: CDpsUgXN2G
- enabled: true
name: lastname
value: Meinl
id: WJrnFEGNTM
- enabled: true
name: password
value: TMm#J0r0iAlKliG6gBP73OnaN#ldOI&x
id: sCrraX5tUu
- enabled: true
name: phoneNumber
value: '+4915202989709'
id: U8B5xaQqXb
color: null
sortPriority: 0.0
+13
View File
@@ -0,0 +1,13 @@
type: folder
model: folder
id: fl_gBnxCkQD7y
createdAt: 2025-07-24T10:00:13.759993700
updatedAt: 2025-07-24T10:00:13.759994200
workspaceId: wk_gViMDNsLC9
folderId: null
authentication: {}
authenticationType: null
description: ''
headers: []
name: Auth
sortPriority: -1753351200000.0
+31
View File
@@ -0,0 +1,31 @@
type: http_request
model: http_request
id: rq_eRxcYSQsbe
createdAt: 2026-02-15T19:37:26.962157100
updatedAt: 2026-02-15T19:39:42.170904100
workspaceId: wk_gViMDNsLC9
folderId: fl_gBnxCkQD7y
authentication: {}
authenticationType: null
body:
text: |-
{
"firstName": "${[ firstname ]}",
"lastName": "${[ lastname ]}",
"email": "${[ email ]}",
"phoneNumber": "${[ phoneNumber ]}",
"country": "de",
"password": "${[ password ]}"
}
bodyType: application/json
description: ''
headers:
- enabled: true
name: Content-Type
value: application/json
id: d7yQvygRwt
method: POST
name: Register
sortPriority: -1753351216695.0
url: ${[ BASE_URL ]}/auth/register
urlParameters: []
+27
View File
@@ -0,0 +1,27 @@
type: http_request
model: http_request
id: rq_qMQNa95s3H
createdAt: 2025-07-24T10:00:16.697661100
updatedAt: 2025-07-25T17:35:16.939160200
workspaceId: wk_gViMDNsLC9
folderId: fl_gBnxCkQD7y
authentication: {}
authenticationType: null
body:
text: |-
{
"email":"${[ email ]}",
"password":"${[ password ]}"
}
bodyType: application/json
description: ''
headers:
- enabled: true
name: Content-Type
value: application/json
id: bMujbbncVX
method: POST
name: Login
sortPriority: -1753351216695.0
url: ${[ BASE_URL ]}/auth/login
urlParameters: []
+14
View File
@@ -0,0 +1,14 @@
type: workspace
model: workspace
id: wk_gViMDNsLC9
createdAt: 2025-07-24T09:59:20.801215
updatedAt: 2025-07-24T09:59:22.805146400
authentication: {}
authenticationType: null
description: ''
headers: []
name: FlightScore
encryptionKeyChallenge: eUE0azNuQwGJlYhActHWAdNJxx20lcK8NFEhIOxgSadHRtl/TBAjdViFGuBpTqe2IxTYA9GbNSNeofdXpXD7psJqTH/UsWrzK2mqE7MwUbClYLVwi26gDREvzu7TK+aTJiA=
settingValidateCertificates: true
settingFollowRedirects: true
settingRequestTimeout: 0