From 4a8fb80411ef4f140e1d90d377657f5f70e53ed1 Mon Sep 17 00:00:00 2001 From: Kavin <20838718+FireMasterK@users.noreply.github.com> Date: Sun, 30 Oct 2022 21:40:31 +0000 Subject: [PATCH] Implement status code error errors. --- .../me/kavin/piped/server/ServerLauncher.java | 8 +++- .../server/handlers/ChannelHandlers.java | 12 ++++-- .../server/handlers/PlaylistHandlers.java | 10 +++-- .../piped/server/handlers/SearchHandlers.java | 10 +++-- .../piped/server/handlers/StreamHandlers.java | 2 +- .../server/handlers/TrendingHandlers.java | 4 +- .../handlers/auth/AuthPlaylistHandlers.java | 34 ++++++++++------ .../server/handlers/auth/FeedHandlers.java | 40 +++++++++++-------- .../server/handlers/auth/UserHandlers.java | 38 ++++++++---------- .../me/kavin/piped/utils/ErrorResponse.java | 40 +++++++++++++++++++ .../kavin/piped/utils/ExceptionHandler.java | 18 +++++++++ .../me/kavin/piped/utils/IStatusCode.java | 10 +++++ .../kavin/piped/utils/SponsorBlockUtils.java | 13 ++++-- .../utils/resp/AlreadyRegisteredResponse.java | 8 +++- .../resp/AuthenticationFailureResponse.java | 8 +++- .../resp/CompromisedPasswordResponse.java | 8 +++- .../resp/DisabledRegistrationResponse.java | 8 +++- .../kavin/piped/utils/resp/ErrorResponse.java | 11 ----- .../resp/IncorrectCredentialsResponse.java | 8 +++- .../utils/resp/InvalidRequestResponse.java | 18 ++++++++- .../piped/utils/resp/SimpleErrorMessage.java | 10 +++++ .../piped/utils/resp/StackTraceResponse.java | 5 +++ 22 files changed, 236 insertions(+), 87 deletions(-) create mode 100644 src/main/java/me/kavin/piped/utils/ErrorResponse.java create mode 100644 src/main/java/me/kavin/piped/utils/IStatusCode.java delete mode 100644 src/main/java/me/kavin/piped/utils/resp/ErrorResponse.java create mode 100644 src/main/java/me/kavin/piped/utils/resp/SimpleErrorMessage.java create mode 100644 src/main/java/me/kavin/piped/utils/resp/StackTraceResponse.java diff --git a/src/main/java/me/kavin/piped/server/ServerLauncher.java b/src/main/java/me/kavin/piped/server/ServerLauncher.java index b7c0b63..a0bdd98 100644 --- a/src/main/java/me/kavin/piped/server/ServerLauncher.java +++ b/src/main/java/me/kavin/piped/server/ServerLauncher.java @@ -19,8 +19,8 @@ import me.kavin.piped.server.handlers.auth.FeedHandlers; import me.kavin.piped.server.handlers.auth.UserHandlers; import me.kavin.piped.utils.*; import me.kavin.piped.utils.resp.DeleteUserRequest; -import me.kavin.piped.utils.resp.ErrorResponse; import me.kavin.piped.utils.resp.LoginRequest; +import me.kavin.piped.utils.resp.StackTraceResponse; import me.kavin.piped.utils.resp.SubscriptionUpdateRequest; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.exception.ExceptionUtils; @@ -455,9 +455,13 @@ public class ServerLauncher extends MultithreadedHttpServerLauncher { e = ExceptionHandler.handle(e, path); + if (e instanceof ErrorResponse error) { + return getJsonResponse(error.getCode(), error.getContent(), "private"); + } + try { return getJsonResponse(500, Constants.mapper - .writeValueAsBytes(new ErrorResponse(ExceptionUtils.getStackTrace(e), e.getMessage())), "private"); + .writeValueAsBytes(new StackTraceResponse(ExceptionUtils.getStackTrace(e), e.getMessage())), "private"); } catch (JsonProcessingException ex) { return HttpResponse.ofCode(500); } diff --git a/src/main/java/me/kavin/piped/server/handlers/ChannelHandlers.java b/src/main/java/me/kavin/piped/server/handlers/ChannelHandlers.java index bfcf88e..e767bda 100644 --- a/src/main/java/me/kavin/piped/server/handlers/ChannelHandlers.java +++ b/src/main/java/me/kavin/piped/server/handlers/ChannelHandlers.java @@ -139,10 +139,13 @@ public class ChannelHandlers { throws IOException, ExtractionException { if (StringUtils.isEmpty(prevpageStr)) - return mapper.writeValueAsBytes(new InvalidRequestResponse()); + ExceptionHandler.throwErrorResponse(new InvalidRequestResponse("nextpage is a required parameter")); Page prevpage = mapper.readValue(prevpageStr, Page.class); + if (prevpage == null) + ExceptionHandler.throwErrorResponse(new InvalidRequestResponse("nextpage is a required parameter")); + ListExtractor.InfoItemsPage info = ChannelInfo.getMoreItems(YOUTUBE_SERVICE, "https://youtube.com/channel/" + channelId, prevpage); @@ -164,7 +167,7 @@ public class ChannelHandlers { throws IOException, ExtractionException { if (StringUtils.isEmpty(data)) - return mapper.writeValueAsBytes(new InvalidRequestResponse()); + ExceptionHandler.throwErrorResponse(new InvalidRequestResponse("data is a required parameter")); YouTubeChannelTabHandler tabHandler = mapper.readValue(data, YouTubeChannelTabHandlerMixin.class); @@ -184,12 +187,15 @@ public class ChannelHandlers { public static byte[] channelTabPageResponse(String data, String prevPageStr) throws Exception { if (StringUtils.isEmpty(data)) - return mapper.writeValueAsBytes(new InvalidRequestResponse()); + ExceptionHandler.throwErrorResponse(new InvalidRequestResponse("data is a required parameter")); YouTubeChannelTabHandler tabHandler = mapper.readValue(data, YouTubeChannelTabHandlerMixin.class); Page prevPage = mapper.readValue(prevPageStr, Page.class); + if (prevPage == null) + ExceptionHandler.throwErrorResponse(new InvalidRequestResponse("nextpage is a required parameter")); + var info = ChannelTabInfo.getMoreItems(YOUTUBE_SERVICE, tabHandler, prevPage); String nextpage = null; diff --git a/src/main/java/me/kavin/piped/server/handlers/PlaylistHandlers.java b/src/main/java/me/kavin/piped/server/handlers/PlaylistHandlers.java index 83559a3..0a8a88d 100644 --- a/src/main/java/me/kavin/piped/server/handlers/PlaylistHandlers.java +++ b/src/main/java/me/kavin/piped/server/handlers/PlaylistHandlers.java @@ -9,6 +9,7 @@ import com.rometools.rome.io.SyndFeedOutput; import it.unimi.dsi.fastutil.objects.ObjectArrayList; import me.kavin.piped.consts.Constants; import me.kavin.piped.server.handlers.auth.AuthPlaylistHandlers; +import me.kavin.piped.utils.ExceptionHandler; import me.kavin.piped.utils.obj.ContentItem; import me.kavin.piped.utils.obj.Playlist; import me.kavin.piped.utils.obj.StreamsPage; @@ -35,7 +36,7 @@ public class PlaylistHandlers { public static byte[] playlistResponse(String playlistId) throws ExtractionException, IOException { if (StringUtils.isBlank(playlistId)) - return mapper.writeValueAsBytes(new InvalidRequestResponse()); + ExceptionHandler.throwErrorResponse(new InvalidRequestResponse("playlistId is a required parameter")); if (playlistId.matches("[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}")) return AuthPlaylistHandlers.playlistPipedResponse(playlistId); @@ -70,10 +71,13 @@ public class PlaylistHandlers { throws IOException, ExtractionException { if (StringUtils.isEmpty(prevpageStr)) - return mapper.writeValueAsBytes(new InvalidRequestResponse()); + ExceptionHandler.throwErrorResponse(new InvalidRequestResponse("nextpage is a required parameter")); Page prevpage = mapper.readValue(prevpageStr, Page.class); + if (prevpage == null) + ExceptionHandler.throwErrorResponse(new InvalidRequestResponse("nextpage is a required parameter")); + ListExtractor.InfoItemsPage info = PlaylistInfo.getMoreItems(YOUTUBE_SERVICE, "https://www.youtube.com/playlist?list=" + playlistId, prevpage); @@ -94,7 +98,7 @@ public class PlaylistHandlers { public static byte[] playlistRSSResponse(String playlistId) throws ExtractionException, IOException, FeedException { if (StringUtils.isBlank(playlistId)) - return mapper.writeValueAsBytes(new InvalidRequestResponse()); + ExceptionHandler.throwErrorResponse(new InvalidRequestResponse("playlistId is a required parameter")); if (playlistId.matches("[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}")) return AuthPlaylistHandlers.playlistPipedRSSResponse(playlistId); diff --git a/src/main/java/me/kavin/piped/server/handlers/SearchHandlers.java b/src/main/java/me/kavin/piped/server/handlers/SearchHandlers.java index 5a2aa67..5c33783 100644 --- a/src/main/java/me/kavin/piped/server/handlers/SearchHandlers.java +++ b/src/main/java/me/kavin/piped/server/handlers/SearchHandlers.java @@ -1,5 +1,6 @@ package me.kavin.piped.server.handlers; +import me.kavin.piped.utils.ExceptionHandler; import me.kavin.piped.utils.obj.ContentItem; import me.kavin.piped.utils.obj.SearchResults; import me.kavin.piped.utils.resp.InvalidRequestResponse; @@ -24,7 +25,7 @@ public class SearchHandlers { throws IOException, ExtractionException { if (StringUtils.isEmpty(query)) - return mapper.writeValueAsBytes(new InvalidRequestResponse()); + ExceptionHandler.throwErrorResponse(new InvalidRequestResponse("query is a required parameter")); return mapper.writeValueAsBytes(YOUTUBE_SERVICE.getSuggestionExtractor().suggestionList(query)); @@ -34,7 +35,7 @@ public class SearchHandlers { throws IOException, ExtractionException { if (StringUtils.isEmpty(query)) - return mapper.writeValueAsBytes(new InvalidRequestResponse()); + ExceptionHandler.throwErrorResponse(new InvalidRequestResponse("query is a required parameter")); return mapper.writeValueAsBytes(Arrays.asList( query, @@ -62,10 +63,13 @@ public class SearchHandlers { throws IOException, ExtractionException { if (StringUtils.isEmpty(prevpageStr)) - return mapper.writeValueAsBytes(new InvalidRequestResponse()); + ExceptionHandler.throwErrorResponse(new InvalidRequestResponse("nextpage is a required parameter")); Page prevpage = mapper.readValue(prevpageStr, Page.class); + if (prevpage == null) + ExceptionHandler.throwErrorResponse(new InvalidRequestResponse("nextpage is a required parameter")); + ListExtractor.InfoItemsPage pages = SearchInfo.getMoreItems(YOUTUBE_SERVICE, YOUTUBE_SERVICE.getSearchQHFactory().fromQuery(q, Collections.singletonList(filter), null), prevpage); diff --git a/src/main/java/me/kavin/piped/server/handlers/StreamHandlers.java b/src/main/java/me/kavin/piped/server/handlers/StreamHandlers.java index 2c223a8..f4b48e5 100644 --- a/src/main/java/me/kavin/piped/server/handlers/StreamHandlers.java +++ b/src/main/java/me/kavin/piped/server/handlers/StreamHandlers.java @@ -212,7 +212,7 @@ public class StreamHandlers { public static byte[] commentsPageResponse(String videoId, String prevpageStr) throws Exception { if (StringUtils.isEmpty(prevpageStr)) - return mapper.writeValueAsBytes(new InvalidRequestResponse()); + ExceptionHandler.throwErrorResponse(new InvalidRequestResponse("nextpage is a required parameter")); Page prevpage = mapper.readValue(prevpageStr, Page.class); diff --git a/src/main/java/me/kavin/piped/server/handlers/TrendingHandlers.java b/src/main/java/me/kavin/piped/server/handlers/TrendingHandlers.java index b8cbbe4..097e677 100644 --- a/src/main/java/me/kavin/piped/server/handlers/TrendingHandlers.java +++ b/src/main/java/me/kavin/piped/server/handlers/TrendingHandlers.java @@ -1,5 +1,6 @@ package me.kavin.piped.server.handlers; +import me.kavin.piped.utils.ExceptionHandler; import me.kavin.piped.utils.obj.ContentItem; import me.kavin.piped.utils.resp.InvalidRequestResponse; import org.schabi.newpipe.extractor.exceptions.ExtractionException; @@ -20,8 +21,7 @@ public class TrendingHandlers { throws ExtractionException, IOException { if (region == null) - return mapper.writeValueAsBytes(new InvalidRequestResponse()); - + ExceptionHandler.throwErrorResponse(new InvalidRequestResponse("region is a required parameter")); KioskList kioskList = YOUTUBE_SERVICE.getKioskList(); kioskList.forceContentCountry(new ContentCountry(region)); diff --git a/src/main/java/me/kavin/piped/server/handlers/auth/AuthPlaylistHandlers.java b/src/main/java/me/kavin/piped/server/handlers/auth/AuthPlaylistHandlers.java index e33157a..4db5f6a 100644 --- a/src/main/java/me/kavin/piped/server/handlers/auth/AuthPlaylistHandlers.java +++ b/src/main/java/me/kavin/piped/server/handlers/auth/AuthPlaylistHandlers.java @@ -13,6 +13,7 @@ import jakarta.persistence.criteria.JoinType; import me.kavin.piped.consts.Constants; import me.kavin.piped.utils.DatabaseHelper; import me.kavin.piped.utils.DatabaseSessionFactory; +import me.kavin.piped.utils.ExceptionHandler; import me.kavin.piped.utils.URLUtils; import me.kavin.piped.utils.obj.ContentItem; import me.kavin.piped.utils.obj.Playlist; @@ -44,6 +45,10 @@ import static me.kavin.piped.utils.URLUtils.substringYouTube; public class AuthPlaylistHandlers { public static byte[] playlistPipedResponse(String playlistId) throws IOException { + + if (StringUtils.isBlank(playlistId)) + ExceptionHandler.throwErrorResponse(new InvalidRequestResponse("playlistId is a required parameter")); + try (StatelessSession s = DatabaseSessionFactory.createStatelessSession()) { var cb = s.getCriteriaBuilder(); var cq = cb.createQuery(me.kavin.piped.utils.obj.db.Playlist.class); @@ -81,6 +86,9 @@ public class AuthPlaylistHandlers { public static byte[] playlistPipedRSSResponse(String playlistId) throws FeedException { + if (StringUtils.isBlank(playlistId)) + ExceptionHandler.throwErrorResponse(new InvalidRequestResponse("playlistId is required parameter")); + try (StatelessSession s = DatabaseSessionFactory.createStatelessSession()) { var cb = s.getCriteriaBuilder(); var cq = cb.createQuery(me.kavin.piped.utils.obj.db.Playlist.class); @@ -121,12 +129,12 @@ public class AuthPlaylistHandlers { public static byte[] createPlaylist(String session, String name) throws IOException { if (StringUtils.isBlank(session) || StringUtils.isBlank(name)) - return mapper.writeValueAsBytes(new InvalidRequestResponse()); + ExceptionHandler.throwErrorResponse(new InvalidRequestResponse("session and name are required parameters")); User user = DatabaseHelper.getUserFromSession(session); if (user == null) - return mapper.writeValueAsBytes(new AuthenticationFailureResponse()); + ExceptionHandler.throwErrorResponse(new AuthenticationFailureResponse()); try (Session s = DatabaseSessionFactory.createSession()) { var playlist = new me.kavin.piped.utils.obj.db.Playlist(name, user, "https://i.ytimg.com/"); @@ -145,12 +153,12 @@ public class AuthPlaylistHandlers { public static byte[] renamePlaylistResponse(String session, String playlistId, String newName) throws IOException { if (StringUtils.isBlank(session) || StringUtils.isBlank(playlistId)) - return mapper.writeValueAsBytes(new InvalidRequestResponse()); + ExceptionHandler.throwErrorResponse(new InvalidRequestResponse("session and playlistId are required parameters")); User user = DatabaseHelper.getUserFromSession(session); if (user == null) - return mapper.writeValueAsBytes(new AuthenticationFailureResponse()); + ExceptionHandler.throwErrorResponse(new AuthenticationFailureResponse()); try (Session s = DatabaseSessionFactory.createSession()) { var playlist = DatabaseHelper.getPlaylistFromId(s, playlistId); @@ -177,12 +185,12 @@ public class AuthPlaylistHandlers { public static byte[] deletePlaylistResponse(String session, String playlistId) throws IOException { if (StringUtils.isBlank(session) || StringUtils.isBlank(playlistId)) - return mapper.writeValueAsBytes(new InvalidRequestResponse()); + ExceptionHandler.throwErrorResponse(new InvalidRequestResponse("session and playlistId are required parameters")); User user = DatabaseHelper.getUserFromSession(session); if (user == null) - return mapper.writeValueAsBytes(new AuthenticationFailureResponse()); + ExceptionHandler.throwErrorResponse(new AuthenticationFailureResponse()); try (Session s = DatabaseSessionFactory.createSession()) { var playlist = DatabaseHelper.getPlaylistFromId(s, playlistId); @@ -207,12 +215,12 @@ public class AuthPlaylistHandlers { public static byte[] addToPlaylistResponse(String session, String playlistId, String videoId) throws IOException, ExtractionException { if (StringUtils.isBlank(session) || StringUtils.isBlank(playlistId) || StringUtils.isBlank(videoId)) - return mapper.writeValueAsBytes(new InvalidRequestResponse()); + ExceptionHandler.throwErrorResponse(new InvalidRequestResponse("session, playlistId and videoId are required parameters")); var user = DatabaseHelper.getUserFromSession(session); if (user == null) - return mapper.writeValueAsBytes(new AuthenticationFailureResponse()); + ExceptionHandler.throwErrorResponse(new AuthenticationFailureResponse()); try (Session s = DatabaseSessionFactory.createSession()) { var cb = s.getCriteriaBuilder(); @@ -268,7 +276,7 @@ public class AuthPlaylistHandlers { public static byte[] removeFromPlaylistResponse(String session, String playlistId, int index) throws IOException { if (StringUtils.isBlank(session) || StringUtils.isBlank(playlistId)) - return mapper.writeValueAsBytes(new InvalidRequestResponse()); + ExceptionHandler.throwErrorResponse(new InvalidRequestResponse("session and playlistId are required parameters")); try (Session s = DatabaseSessionFactory.createSession()) { var cb = s.getCriteriaBuilder(); @@ -304,12 +312,12 @@ public class AuthPlaylistHandlers { public static byte[] importPlaylistResponse(String session, String playlistId) throws IOException, ExtractionException { if (StringUtils.isBlank(session) || StringUtils.isBlank(playlistId)) - return mapper.writeValueAsBytes(new InvalidRequestResponse()); + ExceptionHandler.throwErrorResponse(new InvalidRequestResponse("session and playlistId are required parameters")); var user = DatabaseHelper.getUserFromSession(session); if (user == null) - return mapper.writeValueAsBytes(new AuthenticationFailureResponse()); + ExceptionHandler.throwErrorResponse(new AuthenticationFailureResponse()); final String url = "https://www.youtube.com/playlist?list=" + playlistId; @@ -380,14 +388,14 @@ public class AuthPlaylistHandlers { public static byte[] playlistsResponse(String session) throws IOException { if (StringUtils.isBlank(session)) - return mapper.writeValueAsBytes(new InvalidRequestResponse()); + ExceptionHandler.throwErrorResponse(new InvalidRequestResponse("session is a required parameter")); try (Session s = DatabaseSessionFactory.createSession()) { User user = DatabaseHelper.getUserFromSession(session, s); if (user == null) - return mapper.writeValueAsBytes(new AuthenticationFailureResponse()); + ExceptionHandler.throwErrorResponse(new AuthenticationFailureResponse()); var playlists = new ObjectArrayList<>(); diff --git a/src/main/java/me/kavin/piped/server/handlers/auth/FeedHandlers.java b/src/main/java/me/kavin/piped/server/handlers/auth/FeedHandlers.java index 39c0ae9..2162a4b 100644 --- a/src/main/java/me/kavin/piped/server/handlers/auth/FeedHandlers.java +++ b/src/main/java/me/kavin/piped/server/handlers/auth/FeedHandlers.java @@ -39,7 +39,7 @@ public class FeedHandlers { throws IOException { if (StringUtils.isBlank(session) || StringUtils.isBlank(channelId)) - return mapper.writeValueAsBytes(new InvalidRequestResponse()); + ExceptionHandler.throwErrorResponse(new InvalidRequestResponse("session and channelId are required parameters")); try (Session s = DatabaseSessionFactory.createSession()) { @@ -66,7 +66,8 @@ public class FeedHandlers { } - return mapper.writeValueAsBytes(new AuthenticationFailureResponse()); + ExceptionHandler.throwErrorResponse(new AuthenticationFailureResponse()); + return null; } } @@ -74,7 +75,7 @@ public class FeedHandlers { public static byte[] isSubscribedResponse(String session, String channelId) throws IOException { if (StringUtils.isBlank(session) || StringUtils.isBlank(channelId)) - return mapper.writeValueAsBytes(new InvalidRequestResponse()); + ExceptionHandler.throwErrorResponse(new InvalidRequestResponse("session and channelId are required parameters")); try (StatelessSession s = DatabaseSessionFactory.createStatelessSession()) { var cb = s.getCriteriaBuilder(); @@ -94,7 +95,7 @@ public class FeedHandlers { public static byte[] feedResponse(String session) throws IOException { if (StringUtils.isBlank(session)) - return mapper.writeValueAsBytes(new InvalidRequestResponse()); + ExceptionHandler.throwErrorResponse(new InvalidRequestResponse("session is a required parameter")); User user = DatabaseHelper.getUserFromSession(session); @@ -133,13 +134,14 @@ public class FeedHandlers { } } - return mapper.writeValueAsBytes(new AuthenticationFailureResponse()); + ExceptionHandler.throwErrorResponse(new AuthenticationFailureResponse()); + return null; } - public static byte[] feedResponseRSS(String session) throws IOException, FeedException { + public static byte[] feedResponseRSS(String session) throws FeedException { if (StringUtils.isBlank(session)) - return mapper.writeValueAsBytes(new InvalidRequestResponse()); + ExceptionHandler.throwErrorResponse(new InvalidRequestResponse("session is a required parameter")); User user = DatabaseHelper.getUserFromSession(session); @@ -201,7 +203,8 @@ public class FeedHandlers { } } - return mapper.writeValueAsBytes(new AuthenticationFailureResponse()); + ExceptionHandler.throwErrorResponse(new AuthenticationFailureResponse()); + return null; } public static byte[] unauthenticatedFeedResponse(String[] channelIds) throws Exception { @@ -255,8 +258,7 @@ public class FeedHandlers { .collect(Collectors.toUnmodifiableSet()); if (filtered.isEmpty()) - return mapper.writeValueAsBytes(mapper.createObjectNode() - .put("error", "No valid channel IDs provided")); + ExceptionHandler.throwErrorResponse(new InvalidRequestResponse("No valid channel IDs provided")); try (StatelessSession s = DatabaseSessionFactory.createStatelessSession()) { @@ -378,7 +380,7 @@ public class FeedHandlers { public static byte[] importResponse(String session, String[] channelIds, boolean override) throws IOException { if (StringUtils.isBlank(session)) - return mapper.writeValueAsBytes(new InvalidRequestResponse()); + ExceptionHandler.throwErrorResponse(new InvalidRequestResponse("session is a required parameter")); User user = DatabaseHelper.getUserFromSessionWithSubscribed(session); @@ -422,14 +424,15 @@ public class FeedHandlers { return mapper.writeValueAsBytes(new AcceptedResponse()); } - return mapper.writeValueAsBytes(new AuthenticationFailureResponse()); + ExceptionHandler.throwErrorResponse(new AuthenticationFailureResponse()); + return null; } public static byte[] subscriptionsResponse(String session) throws IOException { if (StringUtils.isBlank(session)) - return mapper.writeValueAsBytes(new InvalidRequestResponse()); + ExceptionHandler.throwErrorResponse(new InvalidRequestResponse("session is a required parameter")); User user = DatabaseHelper.getUserFromSession(session); @@ -461,7 +464,8 @@ public class FeedHandlers { } } - return mapper.writeValueAsBytes(new AuthenticationFailureResponse()); + ExceptionHandler.throwErrorResponse(new AuthenticationFailureResponse()); + return null; } @@ -473,6 +477,9 @@ public class FeedHandlers { .filter(id -> id.matches("[A-Za-z\\d_-]+")) .collect(Collectors.toUnmodifiableSet()); + if (filtered.isEmpty()) + return mapper.writeValueAsBytes(Collections.EMPTY_LIST); + try (StatelessSession s = DatabaseSessionFactory.createStatelessSession()) { CriteriaBuilder cb = s.getCriteriaBuilder(); @@ -498,7 +505,7 @@ public class FeedHandlers { throws IOException { if (StringUtils.isBlank(session) || StringUtils.isBlank(channelId)) - return mapper.writeValueAsBytes(new InvalidRequestResponse()); + ExceptionHandler.throwErrorResponse(new InvalidRequestResponse("session and channelId are required parameters")); User user = DatabaseHelper.getUserFromSession(session); @@ -513,7 +520,8 @@ public class FeedHandlers { } - return mapper.writeValueAsBytes(new AuthenticationFailureResponse()); + ExceptionHandler.throwErrorResponse(new AuthenticationFailureResponse()); + return null; } } diff --git a/src/main/java/me/kavin/piped/server/handlers/auth/UserHandlers.java b/src/main/java/me/kavin/piped/server/handlers/auth/UserHandlers.java index 475ea86..3719767 100644 --- a/src/main/java/me/kavin/piped/server/handlers/auth/UserHandlers.java +++ b/src/main/java/me/kavin/piped/server/handlers/auth/UserHandlers.java @@ -7,12 +7,12 @@ import jakarta.persistence.criteria.Root; import me.kavin.piped.consts.Constants; import me.kavin.piped.utils.DatabaseHelper; import me.kavin.piped.utils.DatabaseSessionFactory; +import me.kavin.piped.utils.ExceptionHandler; import me.kavin.piped.utils.RequestUtils; import me.kavin.piped.utils.obj.db.User; import me.kavin.piped.utils.resp.*; import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.exception.ExceptionUtils; import org.hibernate.Session; import org.hibernate.StatelessSession; import org.springframework.security.crypto.argon2.Argon2PasswordEncoder; @@ -31,16 +31,13 @@ public class UserHandlers { public static byte[] registerResponse(String user, String pass) throws IOException { if (Constants.DISABLE_REGISTRATION) - return mapper.writeValueAsBytes(new DisabledRegistrationResponse()); + ExceptionHandler.throwErrorResponse(new DisabledRegistrationResponse()); if (StringUtils.isBlank(user) || StringUtils.isBlank(pass)) - return mapper.writeValueAsBytes(new InvalidRequestResponse()); + ExceptionHandler.throwErrorResponse(new InvalidRequestResponse()); if (user.length() > 24) - return mapper.writeValueAsBytes( - mapper.createObjectNode() - .put("error", "The username must be less than 24 characters") - ); + ExceptionHandler.throwErrorResponse(new InvalidRequestResponse("The username must be less than 24 characters")); user = user.toLowerCase(); @@ -52,7 +49,7 @@ public class UserHandlers { boolean registered = s.createQuery(cr).uniqueResult() != null; if (registered) - return mapper.writeValueAsBytes(new AlreadyRegisteredResponse()); + ExceptionHandler.throwErrorResponse(new AlreadyRegisteredResponse()); if (Constants.COMPROMISED_PASSWORD_CHECK) { String sha1Hash = DigestUtils.sha1Hex(pass).toUpperCase(); @@ -63,7 +60,7 @@ public class UserHandlers { .split("\n"); for (String entry : entries) if (StringUtils.substringBefore(entry, ":").equals(suffix)) - return mapper.writeValueAsBytes(new CompromisedPasswordResponse()); + ExceptionHandler.throwErrorResponse(new CompromisedPasswordResponse()); } User newuser = new User(user, argon2PasswordEncoder.encode(pass), Set.of()); @@ -87,7 +84,7 @@ public class UserHandlers { throws IOException { if (user == null || pass == null) - return mapper.writeValueAsBytes(new InvalidRequestResponse()); + ExceptionHandler.throwErrorResponse(new InvalidRequestResponse("username and password are required parameters")); user = user.toLowerCase(); @@ -106,33 +103,30 @@ public class UserHandlers { } } - return mapper.writeValueAsBytes(new IncorrectCredentialsResponse()); + ExceptionHandler.throwErrorResponse(new IncorrectCredentialsResponse()); + return null; } } public static byte[] deleteUserResponse(String session, String pass) throws IOException { if (StringUtils.isBlank(session) || StringUtils.isBlank(pass)) - return mapper.writeValueAsBytes(new InvalidRequestResponse()); + ExceptionHandler.throwErrorResponse(new InvalidRequestResponse("session and password are required parameters")); try (Session s = DatabaseSessionFactory.createSession()) { User user = DatabaseHelper.getUserFromSession(session); if (user == null) - return mapper.writeValueAsBytes(new AuthenticationFailureResponse()); + ExceptionHandler.throwErrorResponse(new AuthenticationFailureResponse()); String hash = user.getPassword(); if (!hashMatch(hash, pass)) - return mapper.writeValueAsBytes(new IncorrectCredentialsResponse()); + ExceptionHandler.throwErrorResponse(new IncorrectCredentialsResponse()); - try { - var tr = s.beginTransaction(); - s.remove(user); - tr.commit(); - } catch (Exception e) { - return mapper.writeValueAsBytes(new ErrorResponse(ExceptionUtils.getStackTrace(e), e.getMessage())); - } + var tr = s.beginTransaction(); + s.remove(user); + tr.commit(); return mapper.writeValueAsBytes(new DeleteUserResponse(user.getUsername())); } @@ -141,7 +135,7 @@ public class UserHandlers { public static byte[] logoutResponse(String session) throws JsonProcessingException { if (StringUtils.isBlank(session)) - return mapper.writeValueAsBytes(new InvalidRequestResponse()); + ExceptionHandler.throwErrorResponse(new InvalidRequestResponse("session is a required parameter")); try (StatelessSession s = DatabaseSessionFactory.createStatelessSession()) { var tr = s.beginTransaction(); diff --git a/src/main/java/me/kavin/piped/utils/ErrorResponse.java b/src/main/java/me/kavin/piped/utils/ErrorResponse.java new file mode 100644 index 0000000..f6d394f --- /dev/null +++ b/src/main/java/me/kavin/piped/utils/ErrorResponse.java @@ -0,0 +1,40 @@ +package me.kavin.piped.utils; + +import com.fasterxml.jackson.core.JsonProcessingException; + +import java.io.Serial; + +import static me.kavin.piped.consts.Constants.mapper; + +public class ErrorResponse extends Exception { + + @Serial + private static final long serialVersionUID = 1L; + + private final int code; + + private final byte[] content; + + public ErrorResponse(int code, byte[] content) { + this.code = code; + this.content = content; + } + + public ErrorResponse(IStatusCode statusObj) throws JsonProcessingException { + this.code = statusObj.getStatusCode(); + this.content = mapper.writeValueAsBytes(statusObj); + } + + public ErrorResponse(int code, Object content) throws JsonProcessingException { + this.code = code; + this.content = mapper.writeValueAsBytes(content); + } + + public int getCode() { + return code; + } + + public byte[] getContent() { + return content; + } +} diff --git a/src/main/java/me/kavin/piped/utils/ExceptionHandler.java b/src/main/java/me/kavin/piped/utils/ExceptionHandler.java index c4c1c00..529956f 100644 --- a/src/main/java/me/kavin/piped/utils/ExceptionHandler.java +++ b/src/main/java/me/kavin/piped/utils/ExceptionHandler.java @@ -1,7 +1,9 @@ package me.kavin.piped.utils; +import com.fasterxml.jackson.core.JsonProcessingException; import io.sentry.Sentry; import me.kavin.piped.consts.Constants; +import org.apache.commons.lang3.exception.ExceptionUtils; import org.schabi.newpipe.extractor.exceptions.ContentNotAvailableException; import java.util.concurrent.CompletionException; @@ -29,4 +31,20 @@ public class ExceptionHandler { return e; } + + public static void throwErrorResponse(IStatusCode statusObj) { + try { + ExceptionUtils.rethrow(new ErrorResponse(statusObj)); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + } + + public static void throwErrorResponse(int code, Object content) { + try { + ExceptionUtils.rethrow(new ErrorResponse(code, content)); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + } } diff --git a/src/main/java/me/kavin/piped/utils/IStatusCode.java b/src/main/java/me/kavin/piped/utils/IStatusCode.java new file mode 100644 index 0000000..f39b15b --- /dev/null +++ b/src/main/java/me/kavin/piped/utils/IStatusCode.java @@ -0,0 +1,10 @@ +package me.kavin.piped.utils; + +import com.fasterxml.jackson.annotation.JsonIgnore; + +public interface IStatusCode { + + @JsonIgnore + public int getStatusCode(); + +} diff --git a/src/main/java/me/kavin/piped/utils/SponsorBlockUtils.java b/src/main/java/me/kavin/piped/utils/SponsorBlockUtils.java index 010f49f..6af6840 100644 --- a/src/main/java/me/kavin/piped/utils/SponsorBlockUtils.java +++ b/src/main/java/me/kavin/piped/utils/SponsorBlockUtils.java @@ -1,8 +1,12 @@ package me.kavin.piped.utils; -import com.grack.nanojson.*; +import com.grack.nanojson.JsonArray; +import com.grack.nanojson.JsonObject; +import com.grack.nanojson.JsonParser; +import com.grack.nanojson.JsonWriter; import me.kavin.piped.consts.Constants; import me.kavin.piped.utils.resp.InvalidRequestResponse; +import me.kavin.piped.utils.resp.SimpleErrorMessage; import org.apache.commons.lang3.StringUtils; import java.io.IOException; @@ -13,7 +17,7 @@ import java.security.NoSuchAlgorithmException; public class SponsorBlockUtils { public static String getSponsors(String id, String categories) - throws IOException, NoSuchAlgorithmException, JsonParserException { + throws IOException, NoSuchAlgorithmException { if (StringUtils.isEmpty(categories)) return Constants.mapper.writeValueAsString(new InvalidRequestResponse()); @@ -35,8 +39,9 @@ public class SponsorBlockUtils { } } - return Constants.mapper.writeValueAsString(Constants.mapper.createObjectNode() - .put("error", "All SponsorBlock servers are down")); + ExceptionHandler.throwErrorResponse(new SimpleErrorMessage("All SponsorBlock servers are down")); + + return null; } private static String toSha256(final String videoId) throws NoSuchAlgorithmException { diff --git a/src/main/java/me/kavin/piped/utils/resp/AlreadyRegisteredResponse.java b/src/main/java/me/kavin/piped/utils/resp/AlreadyRegisteredResponse.java index c3bc0d7..66faedf 100644 --- a/src/main/java/me/kavin/piped/utils/resp/AlreadyRegisteredResponse.java +++ b/src/main/java/me/kavin/piped/utils/resp/AlreadyRegisteredResponse.java @@ -1,7 +1,13 @@ package me.kavin.piped.utils.resp; -public class AlreadyRegisteredResponse { +import me.kavin.piped.utils.IStatusCode; + +public class AlreadyRegisteredResponse implements IStatusCode { public String error = "The username you have used is already taken."; + @Override + public int getStatusCode() { + return 400; + } } diff --git a/src/main/java/me/kavin/piped/utils/resp/AuthenticationFailureResponse.java b/src/main/java/me/kavin/piped/utils/resp/AuthenticationFailureResponse.java index ee47766..977c6cc 100644 --- a/src/main/java/me/kavin/piped/utils/resp/AuthenticationFailureResponse.java +++ b/src/main/java/me/kavin/piped/utils/resp/AuthenticationFailureResponse.java @@ -1,7 +1,13 @@ package me.kavin.piped.utils.resp; -public class AuthenticationFailureResponse { +import me.kavin.piped.utils.IStatusCode; + +public class AuthenticationFailureResponse implements IStatusCode { public String error = "An invalid Session ID was provided."; + @Override + public int getStatusCode() { + return 401; + } } diff --git a/src/main/java/me/kavin/piped/utils/resp/CompromisedPasswordResponse.java b/src/main/java/me/kavin/piped/utils/resp/CompromisedPasswordResponse.java index 3d6bae5..ef4c84d 100644 --- a/src/main/java/me/kavin/piped/utils/resp/CompromisedPasswordResponse.java +++ b/src/main/java/me/kavin/piped/utils/resp/CompromisedPasswordResponse.java @@ -1,7 +1,13 @@ package me.kavin.piped.utils.resp; -public class CompromisedPasswordResponse { +import me.kavin.piped.utils.IStatusCode; + +public class CompromisedPasswordResponse implements IStatusCode { public String error = "The password you have entered has already been compromised."; + @Override + public int getStatusCode() { + return 400; + } } diff --git a/src/main/java/me/kavin/piped/utils/resp/DisabledRegistrationResponse.java b/src/main/java/me/kavin/piped/utils/resp/DisabledRegistrationResponse.java index af53f0b..f7160df 100644 --- a/src/main/java/me/kavin/piped/utils/resp/DisabledRegistrationResponse.java +++ b/src/main/java/me/kavin/piped/utils/resp/DisabledRegistrationResponse.java @@ -1,7 +1,13 @@ package me.kavin.piped.utils.resp; -public class DisabledRegistrationResponse { +import me.kavin.piped.utils.IStatusCode; + +public class DisabledRegistrationResponse implements IStatusCode { public String error = "This instance has registrations disabled."; + @Override + public int getStatusCode() { + return 400; + } } diff --git a/src/main/java/me/kavin/piped/utils/resp/ErrorResponse.java b/src/main/java/me/kavin/piped/utils/resp/ErrorResponse.java deleted file mode 100644 index 24dad5a..0000000 --- a/src/main/java/me/kavin/piped/utils/resp/ErrorResponse.java +++ /dev/null @@ -1,11 +0,0 @@ -package me.kavin.piped.utils.resp; - -public class ErrorResponse { - - public final String error, message; - - public ErrorResponse(String error, String message) { - this.error = error; - this.message = message; - } -} diff --git a/src/main/java/me/kavin/piped/utils/resp/IncorrectCredentialsResponse.java b/src/main/java/me/kavin/piped/utils/resp/IncorrectCredentialsResponse.java index 990c324..dc777c1 100644 --- a/src/main/java/me/kavin/piped/utils/resp/IncorrectCredentialsResponse.java +++ b/src/main/java/me/kavin/piped/utils/resp/IncorrectCredentialsResponse.java @@ -1,7 +1,13 @@ package me.kavin.piped.utils.resp; -public class IncorrectCredentialsResponse { +import me.kavin.piped.utils.IStatusCode; + +public class IncorrectCredentialsResponse implements IStatusCode { public String error = "The username or password you have entered is incorrect."; + @Override + public int getStatusCode() { + return 401; + } } diff --git a/src/main/java/me/kavin/piped/utils/resp/InvalidRequestResponse.java b/src/main/java/me/kavin/piped/utils/resp/InvalidRequestResponse.java index 0a6ab77..b3d1aee 100644 --- a/src/main/java/me/kavin/piped/utils/resp/InvalidRequestResponse.java +++ b/src/main/java/me/kavin/piped/utils/resp/InvalidRequestResponse.java @@ -1,7 +1,21 @@ package me.kavin.piped.utils.resp; -public class InvalidRequestResponse { +import me.kavin.piped.utils.IStatusCode; - public String error = "Invalid request sent."; +public class InvalidRequestResponse implements IStatusCode { + public String error; + + public InvalidRequestResponse(String error) { + this.error = error; + } + + public InvalidRequestResponse() { + this.error = "Invalid request sent."; + } + + @Override + public int getStatusCode() { + return 400; + } } diff --git a/src/main/java/me/kavin/piped/utils/resp/SimpleErrorMessage.java b/src/main/java/me/kavin/piped/utils/resp/SimpleErrorMessage.java new file mode 100644 index 0000000..544c8b4 --- /dev/null +++ b/src/main/java/me/kavin/piped/utils/resp/SimpleErrorMessage.java @@ -0,0 +1,10 @@ +package me.kavin.piped.utils.resp; + +import me.kavin.piped.utils.IStatusCode; + +public record SimpleErrorMessage(String error) implements IStatusCode { + @Override + public int getStatusCode() { + return 500; + } +} diff --git a/src/main/java/me/kavin/piped/utils/resp/StackTraceResponse.java b/src/main/java/me/kavin/piped/utils/resp/StackTraceResponse.java new file mode 100644 index 0000000..eac141e --- /dev/null +++ b/src/main/java/me/kavin/piped/utils/resp/StackTraceResponse.java @@ -0,0 +1,5 @@ +package me.kavin.piped.utils.resp; + +public record StackTraceResponse(String error, String message) { + +}