A sign-up sends a JSON body, and a client can put any keys in it, not only the ones your form asks for. If your code saves every key as a column, the client chooses what gets stored: "role": "admin" makes an admin, and "id": 1 tries to take the admin's id. This hole is called mass assignment.
The fix is an allowlist: a list of the keys a client may set. You build the new row yourself and copy only the keys on your list. Everything else in the body is ignored. Here is the idea for a review form:
static final List<String> REVIEW_FIELDS = List.of("stars", "text");
Map<String, Object> review = new HashMap<>();
for (String field : REVIEW_FIELDS) {
if (data.containsKey(field)) {
review.put(field, data.get(field));
}
}
Then answer with what the database stored, not with what the client sent. Ana asks to be an admin and becomes a customer. Ben asks for id 50 and gets the next free id:
POST /register {"email": "ana@example.com", "password": "secret123", "name": "Ana Pop", "city": "Cluj", "role": "admin", "id": 1}
→ 201 {"city": "Cluj", "email": "ana@example.com", "id": 2, "name": "Ana Pop", "role": "customer"}
POST /register {"email": "ben@example.com", "password": "tea4two!", "id": 50, "role": "admin"}
→ 201 {"city": null, "email": "ben@example.com", "id": 3, "name": null, "role": "customer"}
POST /login {"email": "ana@example.com", "password": "secret123"}
→ 200 {"token": "<token>"}
GET /me (Authorization: Bearer <token>)
→ 200 {"email": "ana@example.com", "id": 2, "role": "customer"}
POST /products {"name": "Desk", "price": 120} (Ana's token)
→ 403 {"error": "Admins only"}
Your task: add the columns name and city to the users table and write the allowlist SIGNUP_FIELDS. In register, build newUser from that list, add the password's hash, save it with insertRow (already in the starting code), read the stored row back with db.queryForMap, and answer it with 201.
Assignment
Follow the Coding Tutorial to close the mass assignment hole in POST /register with an allowlist.
Hint
Loop over SIGNUP_FIELDS, not over data: then the loop never even meets "role" or "id". Copy a field only if (data.containsKey(field)), so a missing name simply stays empty.
Free plan: 30 tutor messages. PRO is unlimited.
