|
|
|
|
@ -55,11 +55,11 @@ public class LegacyApi extends BaseHandler {
@@ -55,11 +55,11 @@ public class LegacyApi extends BaseHandler {
|
|
|
|
|
allowOrigin(ex, "*"); // add CORS header
|
|
|
|
|
yield load(path,ex); |
|
|
|
|
} |
|
|
|
|
case LOGIN -> getLegacyLogin(ex); |
|
|
|
|
case LOGIN -> getLogin(ex); |
|
|
|
|
case LOGOUT-> logout(ex); |
|
|
|
|
case SEARCH -> { |
|
|
|
|
try { |
|
|
|
|
yield legacySearch(ex); |
|
|
|
|
yield search(ex); |
|
|
|
|
} catch (UmbrellaException e){ |
|
|
|
|
yield send(ex,e); |
|
|
|
|
} |
|
|
|
|
@ -68,54 +68,21 @@ public class LegacyApi extends BaseHandler {
@@ -68,54 +68,21 @@ public class LegacyApi extends BaseHandler {
|
|
|
|
|
}; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private boolean legacySearch(HttpExchange ex) throws IOException, UmbrellaException { |
|
|
|
|
var optToken = SessionToken.from(ex).map(Token::of); |
|
|
|
|
if (optToken.isEmpty()) return sendEmptyResponse(HTTP_UNAUTHORIZED,ex); |
|
|
|
|
var token = optToken.get(); |
|
|
|
|
var params = queryParam(ex); |
|
|
|
|
var key = params.get(KEY) instanceof String s ? s : null; |
|
|
|
|
if (key == null) throw new UmbrellaException(HTTP_BAD_REQUEST,"Missing search key"); |
|
|
|
|
var fulltext = key.contains("+") || "on".equals(params.get("fulltext")); |
|
|
|
|
StringBuilder searchResult = new StringBuilder(); |
|
|
|
|
if (fulltext){ |
|
|
|
|
|
|
|
|
|
for (var module : config.keys()){ |
|
|
|
|
var baseUrl = config.get(module + ".baseUrl"); |
|
|
|
|
if (baseUrl.isEmpty()) continue; |
|
|
|
|
|
|
|
|
|
var res = request(baseUrl.get()+"/search",token.asMap().plus(KEY,key),MIME_FORM_URL,token.asBearer()); |
|
|
|
|
if (!(res instanceof String content) || content.isBlank()) continue; |
|
|
|
|
searchResult.append("<fieldset><label>").append(module).append("</label>"); |
|
|
|
|
searchResult.append(content).append("</fieldset>\n"); |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
var bookmark = config.get("bookmark.baseUrl"); |
|
|
|
|
if (bookmark.isEmpty()) throw new UmbrellaException(500,"Tag search not available: Bookmark module not configured!"); |
|
|
|
|
var res = request(bookmark.get()+"/search",token.asMap().plus(KEY,key),MIME_FORM_URL,null); |
|
|
|
|
if (!(res instanceof String content)) throw new UmbrellaException(500,"Search did not return html content!"); |
|
|
|
|
searchResult.append(content); |
|
|
|
|
} |
|
|
|
|
return sendContent(ex,searchResult.toString()); |
|
|
|
|
@Override |
|
|
|
|
public boolean doPost(Path path, HttpExchange ex) throws IOException { |
|
|
|
|
try { |
|
|
|
|
return switch (path.pop()) { |
|
|
|
|
case JSON -> jsonUser(ex); |
|
|
|
|
case NOTIFY -> legacyNotify(ex); |
|
|
|
|
case VALIDATE_TOKEN -> validateToken(ex); |
|
|
|
|
default -> super.doPost(path,ex); |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
private boolean logout(HttpExchange ex) throws IOException { |
|
|
|
|
var returnTo = queryParam(ex).get("returnTo"); |
|
|
|
|
var optToken = SessionToken.from(ex).map(Token::of); |
|
|
|
|
if (optToken.isPresent()) try{ |
|
|
|
|
var token = optToken.get(); |
|
|
|
|
users.dropSession(token); |
|
|
|
|
var expiredToken = new SessionToken(token.toString(),"/", Instant.now().minus(1, DAYS),true); |
|
|
|
|
expiredToken.addTo(ex); |
|
|
|
|
if (returnTo instanceof String location) return sendRedirect(ex,location); |
|
|
|
|
return sendContent(ex, Map.of(REDIRECT,"home")); |
|
|
|
|
} catch (UmbrellaException e) { |
|
|
|
|
} catch (UmbrellaException e){ |
|
|
|
|
return send(ex,e); |
|
|
|
|
} |
|
|
|
|
if (returnTo instanceof String location) return sendRedirect(ex,location); |
|
|
|
|
return sendContent(ex,Map.of(REDIRECT,"home")); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private boolean getLegacyLogin(HttpExchange ex) throws IOException { |
|
|
|
|
private boolean getLogin(HttpExchange ex) throws IOException { |
|
|
|
|
var returnTo = queryParam(ex).get("returnTo"); |
|
|
|
|
if (returnTo instanceof String url) { |
|
|
|
|
var token = SessionToken.from(ex).map(SessionToken::sessionId) |
|
|
|
|
@ -131,58 +98,7 @@ public class LegacyApi extends BaseHandler {
@@ -131,58 +98,7 @@ public class LegacyApi extends BaseHandler {
|
|
|
|
|
return sendRedirect(ex,location); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
public boolean doPost(Path path, HttpExchange ex) throws IOException { |
|
|
|
|
try { |
|
|
|
|
return switch (path.pop()) { |
|
|
|
|
case JSON -> legacyJson(ex); |
|
|
|
|
case NOTIFY -> legacyNotify(ex); |
|
|
|
|
case VALIDATE_TOKEN -> validateToken(ex); |
|
|
|
|
default -> super.doPost(path,ex); |
|
|
|
|
}; |
|
|
|
|
} catch (UmbrellaException e){ |
|
|
|
|
return send(ex,e); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private static String stripTrailingSlash(Object o){ |
|
|
|
|
String url = o.toString(); |
|
|
|
|
if (url.endsWith("/")) return url.substring(0,url.length()-1); |
|
|
|
|
return url; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private boolean validateToken(HttpExchange ex) throws UmbrellaException, IOException { |
|
|
|
|
String body; |
|
|
|
|
try { |
|
|
|
|
body = body(ex); |
|
|
|
|
} catch (Exception e){ |
|
|
|
|
throw new UmbrellaException(400,"Failed to read request body").causedBy(e); |
|
|
|
|
} |
|
|
|
|
var map = decode(body); |
|
|
|
|
LOG.log(DEBUG,"validateToken(…, {0}), data: {1}",map); |
|
|
|
|
|
|
|
|
|
String domain = stripTrailingSlash(map.get(DOMAIN) instanceof String s ? s : ""); |
|
|
|
|
var keys = config.keys(); |
|
|
|
|
var match = false; |
|
|
|
|
for (var key : keys){ |
|
|
|
|
var baseUrl = config.get(key + ".baseUrl").map(LegacyApi::stripTrailingSlash).orElse(null); |
|
|
|
|
if (domain.equals(baseUrl)){ |
|
|
|
|
match = true; |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if (!match) throw new UmbrellaException(500,"Failed to verify request domain!"); |
|
|
|
|
|
|
|
|
|
var o = map.get(TOKEN); |
|
|
|
|
if (!(o instanceof String token)) throw new UmbrellaException(500,"Request did not contain token!"); |
|
|
|
|
var session = users.load(Token.of(token)); |
|
|
|
|
var user = users.load(session); |
|
|
|
|
var userMap = user.toMap(); |
|
|
|
|
userMap.put(TOKEN,Map.of(TOKEN,token,EXPIRATION,session.expiration().getEpochSecond())); |
|
|
|
|
return sendContent(ex,userMap); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private boolean legacyJson(HttpExchange ex) throws UmbrellaException, IOException { |
|
|
|
|
private boolean jsonUser(HttpExchange ex) throws UmbrellaException, IOException { |
|
|
|
|
Map<String, Object> data = null; |
|
|
|
|
try { |
|
|
|
|
data = decode(body(ex)); |
|
|
|
|
@ -228,12 +144,6 @@ public class LegacyApi extends BaseHandler {
@@ -228,12 +144,6 @@ public class LegacyApi extends BaseHandler {
|
|
|
|
|
return sendContent(ex, userList.getFirst().toMap()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
protected Session requestSession(Token token) throws UmbrellaException { |
|
|
|
|
var session = users.load(token); |
|
|
|
|
session = users.extend(session); |
|
|
|
|
return session; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private boolean legacyNotify(HttpExchange ex) throws UmbrellaException, IOException { |
|
|
|
|
if (messageUrl == null) throw new UmbrellaException(500,"missing configuration: umbrella.modules.message.baseUrl"); |
|
|
|
|
var mime = contentType(ex).orElse(null); |
|
|
|
|
@ -291,4 +201,94 @@ public class LegacyApi extends BaseHandler {
@@ -291,4 +201,94 @@ public class LegacyApi extends BaseHandler {
|
|
|
|
|
// TODO: should we return json?
|
|
|
|
|
return sendEmptyResponse(HTTP_OK,ex); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private boolean logout(HttpExchange ex) throws IOException { |
|
|
|
|
var returnTo = queryParam(ex).get("returnTo"); |
|
|
|
|
var optToken = SessionToken.from(ex).map(Token::of); |
|
|
|
|
if (optToken.isPresent()) try{ |
|
|
|
|
var token = optToken.get(); |
|
|
|
|
users.dropSession(token); |
|
|
|
|
var expiredToken = new SessionToken(token.toString(),"/", Instant.now().minus(1, DAYS),true); |
|
|
|
|
expiredToken.addTo(ex); |
|
|
|
|
if (returnTo instanceof String location) return sendRedirect(ex,location); |
|
|
|
|
return sendContent(ex, Map.of(REDIRECT,"home")); |
|
|
|
|
} catch (UmbrellaException e) { |
|
|
|
|
return send(ex,e); |
|
|
|
|
} |
|
|
|
|
if (returnTo instanceof String location) return sendRedirect(ex,location); |
|
|
|
|
return sendContent(ex,Map.of(REDIRECT,"home")); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
protected Session requestSession(Token token) throws UmbrellaException { |
|
|
|
|
var session = users.load(token); |
|
|
|
|
session = users.extend(session); |
|
|
|
|
return session; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private boolean search(HttpExchange ex) throws IOException, UmbrellaException { |
|
|
|
|
var optToken = SessionToken.from(ex).map(Token::of); |
|
|
|
|
if (optToken.isEmpty()) return sendEmptyResponse(HTTP_UNAUTHORIZED,ex); |
|
|
|
|
var token = optToken.get(); |
|
|
|
|
var params = queryParam(ex); |
|
|
|
|
var key = params.get(KEY) instanceof String s ? s : null; |
|
|
|
|
if (key == null) throw new UmbrellaException(HTTP_BAD_REQUEST,"Missing search key"); |
|
|
|
|
var fulltext = key.contains("+") || "on".equals(params.get("fulltext")); |
|
|
|
|
StringBuilder searchResult = new StringBuilder(); |
|
|
|
|
if (fulltext){ |
|
|
|
|
for (var module : config.keys()){ |
|
|
|
|
var baseUrl = config.get(module + ".baseUrl"); |
|
|
|
|
if (baseUrl.isEmpty()) continue; |
|
|
|
|
|
|
|
|
|
var res = request(baseUrl.get()+"/search",token.asMap().plus(KEY,key),MIME_FORM_URL,token.asBearer()); |
|
|
|
|
if (!(res instanceof String content) || content.isBlank()) continue; |
|
|
|
|
searchResult.append("<fieldset><label>").append(module).append("</label>"); |
|
|
|
|
searchResult.append(content).append("</fieldset>\n"); |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
var bookmark = config.get("bookmark.baseUrl"); |
|
|
|
|
if (bookmark.isEmpty()) throw new UmbrellaException(500,"Tag search not available: Bookmark module not configured!"); |
|
|
|
|
var res = request(bookmark.get()+"/search",token.asMap().plus(KEY,key),MIME_FORM_URL,null); |
|
|
|
|
if (!(res instanceof String content)) throw new UmbrellaException(500,"Search did not return html content!"); |
|
|
|
|
searchResult.append(content); |
|
|
|
|
} |
|
|
|
|
addCors(ex); |
|
|
|
|
return sendContent(ex,searchResult.toString()); |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
private static String stripTrailingSlash(Object o){ |
|
|
|
|
String url = o.toString(); |
|
|
|
|
if (url.endsWith("/")) return url.substring(0,url.length()-1); |
|
|
|
|
return url; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private boolean validateToken(HttpExchange ex) throws UmbrellaException, IOException { |
|
|
|
|
String body; |
|
|
|
|
try { |
|
|
|
|
body = body(ex); |
|
|
|
|
} catch (Exception e){ |
|
|
|
|
throw new UmbrellaException(400,"Failed to read request body").causedBy(e); |
|
|
|
|
} |
|
|
|
|
var map = decode(body); |
|
|
|
|
LOG.log(DEBUG,"validateToken(…, {0}), data: {1}",map); |
|
|
|
|
|
|
|
|
|
String domain = stripTrailingSlash(map.get(DOMAIN) instanceof String s ? s : ""); |
|
|
|
|
var keys = config.keys(); |
|
|
|
|
var match = false; |
|
|
|
|
for (var key : keys){ |
|
|
|
|
var baseUrl = config.get(key + ".baseUrl").map(LegacyApi::stripTrailingSlash).orElse(null); |
|
|
|
|
if (domain.equals(baseUrl)){ |
|
|
|
|
match = true; |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if (!match) throw new UmbrellaException(500,"Failed to verify request domain!"); |
|
|
|
|
|
|
|
|
|
var o = map.get(TOKEN); |
|
|
|
|
if (!(o instanceof String token)) throw new UmbrellaException(500,"Request did not contain token!"); |
|
|
|
|
var session = users.load(Token.of(token)); |
|
|
|
|
var user = users.load(session); |
|
|
|
|
var userMap = user.toMap(); |
|
|
|
|
userMap.put(TOKEN,Map.of(TOKEN,token,EXPIRATION,session.expiration().getEpochSecond())); |
|
|
|
|
return sendContent(ex,userMap); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|