diff --git a/src/main/java/io/linuxserver/fleet/auth/authenticator/AuthenticatorFactory.java b/src/main/java/io/linuxserver/fleet/auth/authenticator/AuthenticatorFactory.java index 4404a4e..d72e856 100644 --- a/src/main/java/io/linuxserver/fleet/auth/authenticator/AuthenticatorFactory.java +++ b/src/main/java/io/linuxserver/fleet/auth/authenticator/AuthenticatorFactory.java @@ -34,18 +34,17 @@ public class AuthenticatorFactory { AuthenticationType authType = AuthenticationType.valueOf(properties.getAuthenticationType().toUpperCase()); switch (authType) { - case PROPERTIES: - - LOGGER.info("Configuring new authenticator: PropertyLoadedUserAuthenticator"); - return new PropertyLoadedUserAuthenticator(properties.getAppUsername(), properties.getAppPassword()); - case DATABASE: LOGGER.info("Configuring new authenticator: DatabaseStoredUserAuthenticator"); return new DatabaseStoredUserAuthenticator(beans.getPasswordEncoder(), beans.getUserDelegate()); - } - throw new RuntimeException("Provided authentication type is not supported"); + case PROPERTIES: + default: + + LOGGER.info("Configuring new authenticator: PropertyLoadedUserAuthenticator"); + return new PropertyLoadedUserAuthenticator(properties.getAppUsername(), properties.getAppPassword()); + } } public enum AuthenticationType { diff --git a/src/main/java/io/linuxserver/fleet/auth/security/PKCS5S2PasswordEncoder.java b/src/main/java/io/linuxserver/fleet/auth/security/PKCS5S2PasswordEncoder.java index 44051aa..1dd6799 100644 --- a/src/main/java/io/linuxserver/fleet/auth/security/PKCS5S2PasswordEncoder.java +++ b/src/main/java/io/linuxserver/fleet/auth/security/PKCS5S2PasswordEncoder.java @@ -19,6 +19,7 @@ package io.linuxserver.fleet.auth.security; import io.linuxserver.fleet.auth.security.util.SaltGenerator; import org.bouncycastle.crypto.PBEParametersGenerator; +import org.bouncycastle.crypto.digests.SHA256Digest; import org.bouncycastle.crypto.generators.PKCS5S2ParametersGenerator; import org.bouncycastle.crypto.params.KeyParameter; @@ -103,7 +104,7 @@ public class PKCS5S2PasswordEncoder implements PasswordEncoder { */ private byte[] encode(String rawPassword, byte[] salt) { - PKCS5S2ParametersGenerator generator = new PKCS5S2ParametersGenerator(); + PKCS5S2ParametersGenerator generator = new PKCS5S2ParametersGenerator(new SHA256Digest()); generator.init( PBEParametersGenerator.PKCS5PasswordToBytes(rawPassword.toCharArray()), joinArrays(salt, secret), diff --git a/src/main/java/io/linuxserver/fleet/core/FleetApp.java b/src/main/java/io/linuxserver/fleet/core/FleetApp.java index 979ed7b..a8143b7 100644 --- a/src/main/java/io/linuxserver/fleet/core/FleetApp.java +++ b/src/main/java/io/linuxserver/fleet/core/FleetApp.java @@ -17,16 +17,22 @@ package io.linuxserver.fleet.core; -import io.linuxserver.fleet.web.WebServer; +import io.linuxserver.fleet.auth.AuthenticatedUser; +import io.linuxserver.fleet.auth.authenticator.AuthenticatorFactory.AuthenticationType; +import io.linuxserver.fleet.web.JsonTransformer; +import io.linuxserver.fleet.web.SessionAttribute; import io.linuxserver.fleet.web.pages.HomePage; import io.linuxserver.fleet.web.pages.LoginPage; import io.linuxserver.fleet.web.pages.ManageRepositoriesPage; import io.linuxserver.fleet.web.pages.SetupPage; import io.linuxserver.fleet.web.routes.*; import io.linuxserver.fleet.web.websocket.SynchronisationWebSocket; +import spark.Session; import java.util.concurrent.TimeUnit; +import static spark.Spark.*; + /** *
* Primary entry point for the application. All contexts and resources are loaded @@ -73,31 +79,122 @@ public class FleetApp { private void configureWeb() { + port(beans.getProperties().getAppPort()); + + staticFiles.location("/assets"); + staticFiles.expireTime(600); + SynchronisationWebSocket synchronisationWebSocket = new SynchronisationWebSocket(); beans.getSynchronisationDelegate().registerListener(synchronisationWebSocket); - WebServer webServer = beans.getWebServer(); - - webServer.addWebSocket("/admin/ws/sync", synchronisationWebSocket); - webServer.addFilter( "*", new InitialUserFilterRoute(beans.getProperties().getAuthenticationType(), beans.getUserDelegate())); - webServer.start(); - - webServer.addPage( "/", new HomePage(beans.getRepositoryDelegate(), beans.getImageDelegate())); - webServer.addGetApi( "/api/v1/images", new AllImagesApi(beans.getRepositoryDelegate(), beans.getImageDelegate())); - webServer.addPage( "/admin", new ManageRepositoriesPage(beans.getRepositoryDelegate())); - webServer.addPage( "/admin/login", new LoginPage()); - webServer.addPostRoute( "/admin/login", new LoginRoute(beans.getAuthenticationDelegate())); - webServer.addPostRoute( "/admin/logout", new LogoutRoute()); - webServer.addPostApi( "/admin/manageImage", new ManageImageApi(beans.getImageDelegate())); - webServer.addGetApi( "/admin/getImage", new GetImageApi(beans.getImageDelegate())); - webServer.addPostApi( "/admin/manageRepository", new ManageRepositoryApi(beans.getRepositoryDelegate())); - webServer.addPostApi( "/admin/forceSync", new ForceSyncApi(beans.getTaskDelegate())); + webSocket("/admin/ws/sync", synchronisationWebSocket); + init(); + /* + * ----------------------- + * Set Up + * ----------------------- + */ if (initialUserNeedsConfiguring()) { - webServer.addPage( "/setup", new SetupPage()); - webServer.addPostRoute( "/setup", new RegisterInitialUserRoute(beans.getUserDelegate())); + path("/setup", () -> { + + before("", (request, response) -> { + + if (!initialUserNeedsConfiguring()) { + halt(401); + } + }); + + get("", new SetupPage()); + post("", new RegisterInitialUserRoute(beans.getUserDelegate())); + }); } + + /* + * ----------------------- + * Image List + * ----------------------- + */ + get("/", new HomePage(beans.getRepositoryDelegate(), beans.getImageDelegate())); + + /* + * ----------------------- + * API + * ----------------------- + */ + path("/api/v1", () -> { + + get("/images", new AllImagesApi(beans.getRepositoryDelegate(), beans.getImageDelegate()), new JsonTransformer()); + + after("/*", (request, response) -> { + + response.header("Access-Control-Allow-Origin", "*"); + response.header("Access-Control-Allow-Methods", "GET"); + response.header("Content-Type","application/json"); + }); + }); + + /* + * ----------------------- + * Admin + * ----------------------- + */ + path("/admin", () -> { + + before("", (request, response) -> { + + if (initialUserNeedsConfiguring()) { + + response.redirect("/setup"); + + } else { + + Session session = request.session(false); + + if (null == session) + response.redirect("/admin/login"); + + else { + + AuthenticatedUser user = session.attribute(SessionAttribute.USER); + if (null == user) + response.redirect("/admin/login"); + } + } + }); + + before("/*", (request, response) -> { + + if (initialUserNeedsConfiguring()) { + + response.redirect("/setup"); + + } else { + + Session session = request.session(false); + + if (null == session) + response.redirect("/admin/login"); + + else { + + AuthenticatedUser user = session.attribute(SessionAttribute.USER); + if (null == user) + response.redirect("/admin/login"); + } + } + }); + + get("", new ManageRepositoriesPage(beans.getRepositoryDelegate())); + get("/admin/login", new LoginPage()); + post("/admin/login", new LoginRoute(beans.getAuthenticationDelegate())); + post("/logout", new LogoutRoute()); + post("/manageImage", new ManageImageApi(beans.getImageDelegate())); + get("/getImage", new GetImageApi(beans.getImageDelegate())); + post("/manageRepository", new ManageRepositoryApi(beans.getRepositoryDelegate())); + post("/forceSync", new ForceSyncApi(beans.getTaskDelegate())); + }); } private void scheduleSync() { @@ -111,6 +208,10 @@ public class FleetApp { System.setProperty(FLEET_USER_UNDEFINED, String.valueOf(beans.getUserDelegate().isUserRepositoryEmpty())); } - return "true".equalsIgnoreCase(System.getProperty(FLEET_USER_UNDEFINED)); + return "true".equalsIgnoreCase(System.getProperty(FLEET_USER_UNDEFINED)) && databaseAuthenticationEnabled(); + } + + private boolean databaseAuthenticationEnabled() { + return AuthenticationType.DATABASE == AuthenticationType.valueOf(beans.getProperties().getAuthenticationType()); } } diff --git a/src/main/java/io/linuxserver/fleet/core/FleetBeans.java b/src/main/java/io/linuxserver/fleet/core/FleetBeans.java index 032f112..0900980 100644 --- a/src/main/java/io/linuxserver/fleet/core/FleetBeans.java +++ b/src/main/java/io/linuxserver/fleet/core/FleetBeans.java @@ -28,7 +28,6 @@ import io.linuxserver.fleet.db.migration.DatabaseVersion; import io.linuxserver.fleet.delegate.*; import io.linuxserver.fleet.dockerhub.DockerHubV2Client; import io.linuxserver.fleet.thread.TaskManager; -import io.linuxserver.fleet.web.WebServer; /** *
@@ -43,7 +42,6 @@ public class FleetBeans {
private final AuthenticationDelegate authenticationDelegate;
private final DockerHubDelegate dockerHubDelegate;
private final SynchronisationDelegate synchronisationDelegate;
- private final WebServer webServer;
private final TaskManager taskManager;
private final TaskDelegate taskDelegate;
private final UserDelegate userDelegate;
@@ -65,7 +63,6 @@ public class FleetBeans {
imageDelegate = new ImageDelegate(new DefaultImageDAO(databaseConnection));
repositoryDelegate = new RepositoryDelegate(new DefaultRepositoryDAO(databaseConnection));
dockerHubDelegate = new DockerHubDelegate(new DockerHubV2Client(properties.getDockerHubCredentials()));
- webServer = new WebServer(properties.getAppPort());
taskManager = new TaskManager();
synchronisationDelegate = new SynchronisationDelegate(imageDelegate, repositoryDelegate, dockerHubDelegate);
userDelegate = new UserDelegate(passwordEncoder, new DefaultUserDAO(databaseConnection));
@@ -101,10 +98,6 @@ public class FleetBeans {
return databaseVersion;
}
- public WebServer getWebServer() {
- return webServer;
- }
-
public TaskManager getTaskManager() {
return taskManager;
}
diff --git a/src/main/java/io/linuxserver/fleet/web/WebServer.java b/src/main/java/io/linuxserver/fleet/web/WebServer.java
deleted file mode 100644
index dbdcb57..0000000
--- a/src/main/java/io/linuxserver/fleet/web/WebServer.java
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * Copyright (c) 2019 LinuxServer.io
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see