Skip to content

Commit 5b1906e

Browse files
Merge pull request ibi-group#477 from ibi-group/improve-editor-lock
Delete feed lock via navigator.sendBeacon
2 parents 960c1f1 + 5d3b428 commit 5b1906e

File tree

1 file changed

+29
-14
lines changed

1 file changed

+29
-14
lines changed

src/main/java/com/conveyal/datatools/editor/controllers/EditorLockController.java

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,6 @@ public class EditorLockController {
3535

3636

3737
private static String lockFeed (Request req, Response res) {
38-
// FIXME: why is content type not being set in before()/after()?
39-
res.type("application/json");
4038
Auth0UserProfile userProfile = req.attribute("user");
4139
String feedId = req.queryParams("feedId");
4240
EditorSession currentSession = sessionsForFeedIds.get(feedId);
@@ -95,13 +93,10 @@ private static String getLockedFeedMessage(EditorSession session, long minutesUn
9593
private static String invalidateAndCreateNewSession(Request req) {
9694
req.session().invalidate();
9795
Session session = req.session(true);
98-
String newSessionId = session.id();
99-
return newSessionId;
96+
return session.id();
10097
}
10198

10299
private static String maintainLock(Request req, Response res) {
103-
// FIXME: why is content type not being set in before()/after()?
104-
res.type("application/json");
105100
String sessionId = req.params("id");
106101
String feedId = req.queryParams("feedId");
107102
Auth0UserProfile userProfile = req.attribute("user");
@@ -121,7 +116,7 @@ private static String maintainLock(Request req, Response res) {
121116
if (currentSession.userEmail.equals(userProfile.getEmail())) {
122117
// If the new current session is held by this user, give them the option to evict the current session /
123118
// unlock the feed.
124-
LOG.warn("User {} already has an active editor session () for feed {}.", userProfile.getEmail(), currentSession.sessionId, currentSession.feedId);
119+
LOG.warn("User {} already has an active editor session {} for feed {}.", userProfile.getEmail(), currentSession.sessionId, currentSession.feedId);
125120
logMessageAndHalt(req, 400, "Warning! You have an active editing session for this feed underway in a different browser tab.");
126121
} else {
127122
LOG.warn("User {} attempted editor session for feed {} while active session underway for user {}.", userProfile.getEmail(), currentSession.feedId, currentSession.userEmail);
@@ -132,15 +127,26 @@ private static String maintainLock(Request req, Response res) {
132127
// Otherwise, the current session matches the session the user is attempting to maintain. Update the
133128
// lastEdited time.
134129
currentSession.lastCheckIn = System.currentTimeMillis();
135-
// LOG.info("Updating session {} check-in time to {} for user {}", currentSession.sessionId, currentSession.lastCheckIn, currentSession.userEmail);
136130
return formatJSON("Updating time for user " + currentSession.userEmail, 200, feedId, null);
137131
}
138132
}
139133

134+
/**
135+
* Normal path for deleting a feed lock.
136+
*/
140137
private static String deleteFeedLock(Request req, Response res) {
141-
// FIXME: why is content type not being set in before()/after()?
142-
res.type("application/json");
143-
Auth0UserProfile userProfile = req.attribute("user");
138+
return deleteFeedLockCore(req, req.attribute("user"));
139+
}
140+
141+
/**
142+
* Remove a feed lock when a browser calls sendBeacon() when closing/refreshing/navigating away from editor.
143+
*/
144+
private static String deleteFeedLockBeacon(Request req, Response res) {
145+
// The sendBeacon call does not contain any Authorization headers, so we just pass a null userProfile.
146+
return deleteFeedLockCore(req, null);
147+
}
148+
149+
private static String deleteFeedLockCore(Request req, Auth0UserProfile userProfile) {
144150
String feedId = req.queryParams("feedId");
145151
String sessionId = req.params("id");
146152
EditorSession currentSession = sessionsForFeedIds.get(feedId);
@@ -153,8 +159,8 @@ private static String deleteFeedLock(Request req, Response res) {
153159
// Note: There used to be a check here that the requesting user was the same as the user with an open
154160
// session; however, this has been removed because in practice it became a nuisance. Respectful users with
155161
// shared access to a feed can generally be trusted not to boot one another out in a combative manner.
156-
boolean overwrite = Boolean.valueOf(req.queryParams("overwrite"));
157-
if (overwrite) {
162+
boolean overwrite = Boolean.parseBoolean(req.queryParams("overwrite"));
163+
if (userProfile != null && overwrite) {
158164
sessionId = invalidateAndCreateNewSession(req);
159165
EditorSession newEditorSession = new EditorSession(feedId, sessionId, userProfile);
160166
sessionsForFeedIds.put(feedId, newEditorSession);
@@ -165,7 +171,13 @@ private static String deleteFeedLock(Request req, Response res) {
165171
return SparkUtils.formatJSON("Not processing request to delete lock. There is already an active session for user " + currentSession.userEmail, 202);
166172
}
167173
} else {
168-
LOG.info("Current session: {} {}; User session: {} {}", currentSession.userEmail, currentSession.sessionId, userProfile.getEmail(), sessionId);
174+
LOG.info(
175+
"Current session: {} {}; User session: {} {}",
176+
currentSession.userEmail,
177+
currentSession.sessionId,
178+
userProfile != null ? userProfile.getEmail() : "(email unavailable)",
179+
sessionId
180+
);
169181
// Otherwise, the current session matches the session from which the delete request came. This indicates that
170182
// the user's editing session has been closed (by either exiting the editor or closing the browser tab).
171183
LOG.info("Closed session {} for feed {} successfully.", currentSession.sessionId, currentSession.feedId);
@@ -178,6 +190,9 @@ public static void register(String apiPrefix) {
178190
post(apiPrefix + "secure/lock", EditorLockController::lockFeed, json::write);
179191
delete(apiPrefix + "secure/lock/:id", EditorLockController::deleteFeedLock, json::write);
180192
put(apiPrefix + "secure/lock/:id", EditorLockController::maintainLock, json::write);
193+
// Extra, unsecure POST method for removing lock via a browser's Navigator.sendBeacon() method.
194+
// (Navigator.sendBeacon() sends a POST and does not support authorization headers.)
195+
post(apiPrefix + "deletelock/:id", EditorLockController::deleteFeedLockBeacon, json::write);
181196
}
182197

183198
private static String formatJSON(String message, int code, String feedId, String sessionId) {

0 commit comments

Comments
 (0)