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; } }