Skip to content

Commit

Permalink
Merge branch 'main' of https://github.com/lavajuno/fsuvius
Browse files Browse the repository at this point in the history
  • Loading branch information
lavajuno committed Sep 12, 2023
2 parents 679163f + 91abe4e commit 3994a6a
Show file tree
Hide file tree
Showing 7 changed files with 130 additions and 25 deletions.
33 changes: 25 additions & 8 deletions src/main/java/org/jmeifert/fsuvius/FsuviusController.java
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
package org.jmeifert.fsuvius;

import java.awt.*;
import java.time.Duration;
import java.util.List;

import io.github.bucket4j.Bandwidth;
import io.github.bucket4j.Bucket;
import io.github.bucket4j.Refill;
import jakarta.servlet.http.HttpServletRequest;
import org.jmeifert.fsuvius.data.DatabaseController;
import org.jmeifert.fsuvius.error.ForbiddenException;
import org.jmeifert.fsuvius.error.NotFoundException;
import org.jmeifert.fsuvius.error.RateLimitException;
import org.jmeifert.fsuvius.user.User;
import org.jmeifert.fsuvius.util.IPFilter;
import org.jmeifert.fsuvius.util.Log;
import org.springframework.web.bind.annotation.*;

Expand All @@ -19,9 +23,8 @@
@RestController
public class FsuviusController {
private final Log log;

private final Bucket bucket;
private DatabaseController databaseController;
private final DatabaseController databaseController;

/**
* Instantiates a FsuviusController.
Expand Down Expand Up @@ -74,7 +77,10 @@ public List<User> getUsers() {
* @return the new User
*/
@PostMapping("/api/users")
public User newUser(@RequestBody String name) {
public User newUser(@RequestBody String name, HttpServletRequest request) {
if(!IPFilter.checkAddress(request.getRemoteAddr())) {
throw new ForbiddenException(); // reject requests from outside the labs
}
if(bucket.tryConsume(1)) {
log.print("Handling request to create new user with name \"" + name + "\".");
return databaseController.createUser(name);
Expand Down Expand Up @@ -102,7 +108,11 @@ public User getUser(@PathVariable String id) {
* @return The edited User
*/
@PutMapping("/api/users/{id}")
public User editUser(@RequestBody User newUser, @PathVariable String id) {
public User editUser(@RequestBody User newUser,
@PathVariable String id, HttpServletRequest request) {
if(!IPFilter.checkAddress(request.getRemoteAddr())) {
throw new ForbiddenException(); // reject requests from outside the labs
}
if(bucket.tryConsume(1)) {
log.print("Handling request to edit user at ID \"" + id + "\".");
return databaseController.editUser(id, newUser);
Expand All @@ -115,7 +125,10 @@ public User editUser(@RequestBody User newUser, @PathVariable String id) {
* @param id The ID of the user to delete
*/
@DeleteMapping("/api/users/{id}")
public void deleteUser(@PathVariable String id) {
public void deleteUser(@PathVariable String id, HttpServletRequest request) {
if(!IPFilter.checkAddress(request.getRemoteAddr())) {
throw new ForbiddenException(); // reject requests from outside the labs
}
if(bucket.tryConsume(1)) {
log.print("Handling request to delete user at ID \"" + id + "\".");
databaseController.deleteUser(id);
Expand All @@ -127,7 +140,7 @@ public void deleteUser(@PathVariable String id) {
/* ===== PHOTOS ===== */

/**
* Gets a photo by ID.
* Gets a photo by user ID.
* Will result in an HTTP 404 if the photo cannot be found.
* @param id ID of the photo to get
* @return The photo with the specified ID
Expand All @@ -146,12 +159,16 @@ public byte[] getPhoto(@PathVariable String id) {
}

/**
* Updates a photo by ID. Will create it if it does not already exist.
* Updates a photo by user ID. Will create it if it does not already exist.
* @param item New content of the photo
* @param id ID of the photo to update
*/
@PostMapping("api/photos/{id}")
public void putPhoto(@RequestBody String item, @PathVariable String id) {
public void putPhoto(@RequestBody String item,
@PathVariable String id, HttpServletRequest request) {
if(!IPFilter.checkAddress(request.getRemoteAddr())) {
throw new ForbiddenException(); // reject requests from outside the labs
}
if(bucket.tryConsume(1)) {
databaseController.writePhoto(item, id);
return;
Expand Down
16 changes: 13 additions & 3 deletions src/main/java/org/jmeifert/fsuvius/FsuviusMap.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,26 @@ public class FsuviusMap {
/**
* The maximum amount of requests per second the server will handle
* before refusing additional requests.
* Recommended: 50
*/
public static final int MAX_REQUESTS_PER_SECOND = 50;

/**
* The default photo for new users as base64.
* These prefixes specify the addresses to allow edits from.
* (ex. "123.123.123" allows 123.123.123.***, "123.1" allows 123.1**.***.***)
* Any PUT/POST/DELETE/etc. requests from outside the specified range are rejected.
* Recommended: {"128.153.144", "128.153.145", "127.0.0.1"}
*/
public static final String DEFAULT_PHOTO = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAC4jAAAuIwF4pT92AAAE1ElEQVR42u2aSSxzXRjH/17S1xxi2hQxJKKhK7ERmyKIjQS7mrc2iAgLiaRiQcSiIcFGKjEtiBJi2phJDA2q2mKB1jxEWzqdb9d8l1YRXrTnn9zFc+5z7rnnl3Oe+5yndSGEEDix/sDJRQFQABQABUABUAAUAAVAAVAAFIBzyu21m2azGTKZDKurq1hfX4dMJsPGxgbOz88tPru7u4iNjf29BIgNXV1dkaqqKgLg1Wt3d5f8ZlkFoNPpSGlpqd3JOyyAqakpxiRLS0uJVColWq2WmM1m4kiyCqCsrIwBQKVSEUfVCwCPj4+Myefn5xNH1ovPoEajYdhRUVHOlQfo9Xrmd9LNzXnzgI9Kr9dje3sbEokEW1tbkMvlkMvlCAoKQnh4OEJCQuDv74/Q0FCEh4cjMjISbDYbrq6u35MHsNnsN33ynl8KhYKxn8xmM5mfnyfJycnvftbs7KzNfXpwcMDwbW9vf9c+fx7X+Hy+5Z7bJ4JEd3c3iouLnfMssLi4yJg8m82GSCTC/v4+7u7uYDAYYDKZoNVqoVarsba2ho6ODnC53J+VCqtUKsZyEQgEdpeYyWQiWVlZlj6RkZHk4ODgzctzenqaLC8v/94tcHx8jLGxMYstEAgQERHxpr5///5FSkrK794C19fXDDs6Otq5YoDBYGDYOp3OuQD4+Pgw7MHBwRcJlUMDCAsLQ3BwsMUWCoWorq7G3t4ezGaz4wPw9PREc3Mzo621tRWxsbHIzs5GT08Ptra2cHt767ipcF5eHjY3N9HS0sJoF4vFEIvFFpvP5yM1NRUJCQmIiYn59rPGpyVC7u7uaGxsxMDAANhstk2/np4eFBUVIS4uDunp6ZicnHwRRH9tVZjFYiEvLw87OzuYmZlBXV3dqzBmZ2eRnp6OiooK3N/fO05Z3NfXFzweD/X19VAqlVAoFBgfH0dDQ4NVIEKhEAKBAF/1d6XXAvGX/y7AYrEQFRWFjIwM1NbWQi6XY2VlBYWFhQy/pqYm7OzsWH/JP8zXNJlM73qHx8fHf1sPsBcrEhMTweVy4eXlhba2Nsu9zc1NxMXFWe3zf713uzzPVP/pCngNREFBAaNNpVJZ9fXw8GDYEonkXWPJZLKfBwAA/Pz8XhyMbMWUzMxMi93X14ebm5s3jWE0GtHV1fUzAZycnDDs0NBQm75ZWVkMe2Fh4U1jjIyMYGho6GsBjI+PQywW4+rq6s19zs7O0NDQwGhLSEiw6c/j8Rh2ZWUljo6OXq1QTUxMICcn5+sLIr29vRb/srIyMjw8TCQSCTk/PydarZYYjUZiNBrJw8MDUSqVZGBggHC5XMY4nZ2ddosuNTU1jD4cDoeMjo6Ss7MzotfricFgINfX12RtbY1UVFRY/Pr7+20WRD4dwEeu5uZm8vT0ZHeci4sLkpmZ+a5ni0QiotPpbAL41hiQlpaGubk5lJeXg8Vi2fUPDAyESCRCZWWlXd+kpCTMz8+Dz+fDxcXla/OA3Nxc8Hg8qNVqnJyc4PDwEGq1GpeXlzg9PYVUKoW3tzciIiLA4XDA4XAQHx//ocNQQEAAmpqaUFJSgtXVVSwtLUEqlUKj0VgOWMnJyYiPj3+RP1iTC/27vJOLAqAAKAAKgAKgACgACoACoAAoAAqAAqAAnFD/AZZ5oHgkh6rAAAAAAElFTkSuQmCC";
public static final String[] ALLOWED_ADDR_PREFIXES = {"128.153.144", "128.153.145", "127.0.0.1"};

/**
* The maximum allowed size for photos. 1MB recommended.
* The maximum allowed size for photos (in bytes).
* Recommended: 1024 * 1024
*/
public static final int MAX_PHOTO_SIZE = 1024 * 1024;

/**
* The default photo for new users as base64.
*/
public static final String DEFAULT_PHOTO = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAC4jAAAuIwF4pT92AAAE1ElEQVR42u2aSSxzXRjH/17S1xxi2hQxJKKhK7ERmyKIjQS7mrc2iAgLiaRiQcSiIcFGKjEtiBJi2phJDA2q2mKB1jxEWzqdb9d8l1YRXrTnn9zFc+5z7rnnl3Oe+5yndSGEEDix/sDJRQFQABQABUABUAAUAAVAAVAAFIBzyu21m2azGTKZDKurq1hfX4dMJsPGxgbOz88tPru7u4iNjf29BIgNXV1dkaqqKgLg1Wt3d5f8ZlkFoNPpSGlpqd3JOyyAqakpxiRLS0uJVColWq2WmM1m4kiyCqCsrIwBQKVSEUfVCwCPj4+Myefn5xNH1ovPoEajYdhRUVHOlQfo9Xrmd9LNzXnzgI9Kr9dje3sbEokEW1tbkMvlkMvlCAoKQnh4OEJCQuDv74/Q0FCEh4cjMjISbDYbrq6u35MHsNnsN33ynl8KhYKxn8xmM5mfnyfJycnvftbs7KzNfXpwcMDwbW9vf9c+fx7X+Hy+5Z7bJ4JEd3c3iouLnfMssLi4yJg8m82GSCTC/v4+7u7uYDAYYDKZoNVqoVarsba2ho6ODnC53J+VCqtUKsZyEQgEdpeYyWQiWVlZlj6RkZHk4ODgzctzenqaLC8v/94tcHx8jLGxMYstEAgQERHxpr5///5FSkrK794C19fXDDs6Otq5YoDBYGDYOp3OuQD4+Pgw7MHBwRcJlUMDCAsLQ3BwsMUWCoWorq7G3t4ezGaz4wPw9PREc3Mzo621tRWxsbHIzs5GT08Ptra2cHt767ipcF5eHjY3N9HS0sJoF4vFEIvFFpvP5yM1NRUJCQmIiYn59rPGpyVC7u7uaGxsxMDAANhstk2/np4eFBUVIS4uDunp6ZicnHwRRH9tVZjFYiEvLw87OzuYmZlBXV3dqzBmZ2eRnp6OiooK3N/fO05Z3NfXFzweD/X19VAqlVAoFBgfH0dDQ4NVIEKhEAKBAF/1d6XXAvGX/y7AYrEQFRWFjIwM1NbWQi6XY2VlBYWFhQy/pqYm7OzsWH/JP8zXNJlM73qHx8fHf1sPsBcrEhMTweVy4eXlhba2Nsu9zc1NxMXFWe3zf713uzzPVP/pCngNREFBAaNNpVJZ9fXw8GDYEonkXWPJZLKfBwAA/Pz8XhyMbMWUzMxMi93X14ebm5s3jWE0GtHV1fUzAZycnDDs0NBQm75ZWVkMe2Fh4U1jjIyMYGho6GsBjI+PQywW4+rq6s19zs7O0NDQwGhLSEiw6c/j8Rh2ZWUljo6OXq1QTUxMICcn5+sLIr29vRb/srIyMjw8TCQSCTk/PydarZYYjUZiNBrJw8MDUSqVZGBggHC5XMY4nZ2ddosuNTU1jD4cDoeMjo6Ss7MzotfricFgINfX12RtbY1UVFRY/Pr7+20WRD4dwEeu5uZm8vT0ZHeci4sLkpmZ+a5ni0QiotPpbAL41hiQlpaGubk5lJeXg8Vi2fUPDAyESCRCZWWlXd+kpCTMz8+Dz+fDxcXla/OA3Nxc8Hg8qNVqnJyc4PDwEGq1GpeXlzg9PYVUKoW3tzciIiLA4XDA4XAQHx//ocNQQEAAmpqaUFJSgtXVVSwtLUEqlUKj0VgOWMnJyYiPj3+RP1iTC/27vJOLAqAAKAAKgAKgACgACoACoAAoAAqAAqAAnFD/AZZ5oHgkh6rAAAAAAElFTkSuQmCC";
}
20 changes: 20 additions & 0 deletions src/main/java/org/jmeifert/fsuvius/error/ForbiddenAdvice.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package org.jmeifert.fsuvius.error;

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;

/**
* ForbiddenAdvice provides responses to forbidden requests.
*/
@ControllerAdvice
public class ForbiddenAdvice {
@ResponseBody
@ExceptionHandler(ForbiddenException.class)
@ResponseStatus(HttpStatus.FORBIDDEN)
String forbiddenHandler(ForbiddenException e) {
return e.getMessage();
}
}
15 changes: 15 additions & 0 deletions src/main/java/org/jmeifert/fsuvius/error/ForbiddenException.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package org.jmeifert.fsuvius.error;


/**
* A ForbiddenException is thrown when a request attempts to access or modify
* resources it is not allowed to do so with.
*/
public class ForbiddenException extends RuntimeException {
/**
* Throws a ForbiddenException.
*/
public ForbiddenException() {
super("Forbidden.");
}
}
22 changes: 22 additions & 0 deletions src/main/java/org/jmeifert/fsuvius/util/IPFilter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package org.jmeifert.fsuvius.util;

import org.jmeifert.fsuvius.FsuviusMap;

/**
* IPFilter provides functionality for checking IPs against a predefined allow-list.
*/
public class IPFilter {
/**
* Checks an address to see if it is included in the allow-list.
* @param ip The IP address to check
* @return True if the address is included in the allow-list.
*/
public static boolean checkAddress(String ip) {
for(String k : FsuviusMap.ALLOWED_ADDR_PREFIXES) {
if(ip.startsWith(k)) {
return true;
}
}
return false;
}
}
26 changes: 19 additions & 7 deletions src/main/resources/static/editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ function handle_display() {
"Accept": "application/json",
},
}).then(async response => {
if(!response.ok) { throw new Error("GET request failed!"); }
if(!response.ok) { throw new Error(response.status); }
const data = await response.json();
console.log("[DEBUG] Response:");
console.log(data);
Expand Down Expand Up @@ -70,11 +70,15 @@ function handle_save() {
},
body: JSON.stringify(new_user),
}).then(async response => {
if(!response.ok) { throw new Error("PUT request failed!"); }
if(!response.ok) { throw new Error(response.status); }
window.location.href="index.html";
}).catch(error => {
console.log(error);
show_toast("Couldn't save changes. See console for error details.");
if(error.message === "403") {
show_toast("Can't edit outside of the labs.");
} else {
show_toast("Couldn't save changes. See console for error details.");
}
});
}

Expand All @@ -86,11 +90,15 @@ function handle_delete() {
fetch((USER_URL), {
method: "DELETE",
}).then(async response => {
if(!response.ok) { throw new Error("DELETE request failed!"); }
if(!response.ok) { throw new Error(response.status); }
window.location.href="index.html";
}).catch(error => {
console.log(error);
show_toast("Couldn't delete user. See console for error details.");
if(error.message === "403") {
show_toast("Can't edit outside of the labs.");
} else {
show_toast("Couldn't delete user. See console for error details.");
}
});
}
}
Expand All @@ -110,13 +118,17 @@ function handle_upload_photo(input) {
body: event.target.result,
}).then(async response => {
if(!response.ok) {
throw new Error("POST request failed!");
if(!response.ok) { throw new Error(response.status); }
}
show_toast("Photo uploaded.");
document.getElementById("USER_PHOTO").src = event.target.result;
}).catch(error => {
console.log(error);
show_toast("Something went wrong uploading your photo.");
if(error.message === "403") {
show_toast("Can't edit outside of the labs.");
} else {
show_toast("Something went wrong uploading your photo.");
}
});
});
fr.readAsDataURL(input.files[0]);
Expand Down
23 changes: 16 additions & 7 deletions src/main/resources/static/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,16 @@ function handle_create() {
},
body: new_name,
}).then(async response => {
if(!response.ok) { throw new Error("POST request failed!"); }
if(!response.ok) { throw new Error(response.status); }
document.getElementById("CREATE_FIELD").value = "";
display_list();
}).catch(error => {
console.log(error);
show_toast("Failed to create user.");
if(error.message === "403") {
show_toast("Can't edit outside of the labs.");
} else {
show_toast("Failed to create user. See console for error details.");
}
});
}

Expand All @@ -70,7 +74,7 @@ function handle_balance_change(id, offset) {
"Accept": "application/json",
},
}).then(async response => {
if(!response.ok) { throw new Error("GET request failed!"); }
if(!response.ok) { throw new Error(response.status); }
const data = await response.json();
//console.log("[DEBUG] Response:");
//console.log(data);
Expand All @@ -90,20 +94,25 @@ function handle_balance_change(id, offset) {
},
body: JSON.stringify(new_user),
}).then(async response => {
if(!response.ok) { throw new Error("PUT request failed!"); }
console.log(response.status);
if(!response.ok) { throw new Error(response.status); }
const data = await response.json();
//console.log("[DEBUG] Response:");
//console.log(data);
document.getElementById(`USER_BALANCE_${id}`).innerHTML = `${data.balance} FSU`;
show_toast("Changes saved.");
}).catch(error => {
console.log(error);
show_toast("Couldn't save changes. See console for error details.");
if(error.message === "403") {
show_toast("Can't edit outside of the labs.");
} else {
show_toast("Couldn't save changes. See console for error details.");
}
});

}).catch(error => {
console.log(error);
show_toast("Couldn't save changes. See console for error details.");
show_toast("Couldn't get user parameters. See console for error details.");
});
}

Expand All @@ -116,7 +125,7 @@ function display_list() {
"Accept": "application/json",
},
}).then(async response => {
if(!response.ok) { throw new Error("GET request failed!"); }
if(!response.ok) { throw new Error(response.status); }
const data = await response.json();
//console.log("[DEBUG] Response:");
//console.log(data);
Expand Down

0 comments on commit 3994a6a

Please sign in to comment.