Start learning free

LESSON 83 OF 173 · BACKEND: USERS AND SECURITY

Who May Change Things?

Practice

Right now anyone can add a product to the shop: POST /products saves whatever it receives and never asks who is calling. A real shop lets everyone read its products, but only its owner may change them. The simplest way to tell the owner apart: the owner knows a secret, the admin key, and sends it with every request that changes something.

The key travels in a header: a name and a value that go with the request, next to the body. In Spring a method gets the request as an HttpServletRequest request parameter, and request.getHeader("Name") reads one header. It gives null when the request doesn't have that header. Here a made-up route picks its greeting from a header:

@GetMapping("/hello")
Map<String, String> hello(HttpServletRequest request) {
    String lang = request.getHeader("X-Language");
    if ("fr".equals(lang)) {
        return Map.of("message", "Bonjour");
    }
    return Map.of("message", "Hello");
}

When the key is missing or wrong, the shop answers 401 Unauthorized, which means "I don't know who you are", and saves nothing. With the right key, the product is added as before. These are the requests your code must answer:

POST /products  {"name": "Vase", "price": 18}  (no key)                    →  401  {"error": "Missing or unknown key"}
POST /products  {"name": "Vase", "price": 18}  X-Admin-Key: let-me-in      →  401  {"error": "Missing or unknown key"}
POST /products  {"name": "Vase", "price": 18}  X-Admin-Key: admin-key-123  →  201  {"id": 9, "name": "Vase", "price": 18}
GET /products/9   →  200  {"category": null, "id": 9, "name": "Vase", "price": 18, "stock": 0}
GET /products/10  →  404  {"error": "Product not found"}

Your task: store the secret "admin-key-123" in a constant static final String ADMIN_KEY. addProduct already receives the request: as its first line, read the X-Admin-Key header into String key, and when it isn't the admin key, answer {"error": "Missing or unknown key"} with the status 401 before anything is saved.


Assignment
Follow the Coding Tutorial to protect POST /products with the admin key.


Hint
Put the check before the INSERT: once the row is saved, a 401 comes too late. Write the comparison as ADMIN_KEY.equals(key): a missing header gives null, and ADMIN_KEY.equals(null) just answers false, while key.equals(ADMIN_KEY) would throw a NullPointerException.


STEP 1 OF 4
    Ctrl + Enter to check