diff --git a/pom.xml b/pom.xml index f3cc02a6cc8c809c1fc2db1cffc0582dff60199a..9512a90d67ad8a303ba673eb634647852ed03a46 100644 --- a/pom.xml +++ b/pom.xml @@ -31,18 +31,28 @@ <artifactId>jersey-hk2</artifactId> </dependency> - <!-- uncomment this to get JSON support: - <dependency> + <dependency> <groupId>org.glassfish.jersey.media</groupId> - <artifactId>jersey-media-json-binding</artifactId> + <artifactId>jersey-media-json-jackson</artifactId> + <version>${jersey.version}</version> + </dependency> + <dependency> + <groupId>com.fasterxml.jackson.jaxrs</groupId> + <artifactId>jackson-jaxrs-json-provider</artifactId> + <version>2.12.3</version> </dependency> - --> + <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> + <dependency> + <groupId>commons-net</groupId> + <artifactId>commons-net</artifactId> + <version>3.8.0</version> + </dependency> </dependencies> <build> diff --git a/src/main/java/fil/sr2/flopbox/AuthFilter.java b/src/main/java/fil/sr2/flopbox/AuthFilter.java new file mode 100644 index 0000000000000000000000000000000000000000..ce1d73817e9bbebb50d61ec70043e5b1f44fa8e6 --- /dev/null +++ b/src/main/java/fil/sr2/flopbox/AuthFilter.java @@ -0,0 +1,76 @@ +package fil.sr2.flopbox; + +import java.io.IOException; +import java.util.Map; + +import jakarta.annotation.Priority; +import jakarta.ws.rs.Priorities; +import jakarta.ws.rs.core.*; +import jakarta.ws.rs.container.*; +import jakarta.ws.rs.ext.Provider; + +@Provider +@Priority(Priorities.AUTHENTICATION) +public class AuthFilter implements ContainerRequestFilter { + private static final Map<String, String> USERS = loadUsers(); + + /* + * @Override + * public void filter(ContainerRequestContext requestContext) throws IOException + * { + * // Récupération de l'en-tête Authorization (ex. + * "Bearer flopbox-secret-token") + * String authHeader = + * requestContext.getHeaderString(HttpHeaders.AUTHORIZATION); + * if (authHeader == null || !authHeader.startsWith("Bearer ")) { + * requestContext.abortWith( + * Response.status(Response.Status.UNAUTHORIZED) + * .entity("En-tête Authorization manquant ou invalide") + * .build()); + * return; + * } + * String token = authHeader.substring("Bearer ".length()); + * // Vérification du token (dans une implémentation réelle, vérification par + * // rapport à une liste de comptes stockés) + * if (!"flopbox-secret-token".equals(token)) { + * requestContext.abortWith( + * Response.status(Response.Status.UNAUTHORIZED) + * .entity("Token invalide") + * .build()); + * return; + * } + * // Si le token est valide, la requête continue. + * } + */ + + @Override + public void filter(ContainerRequestContext ctx) throws IOException { + String token = extractToken(ctx); + if (!isValidToken(token)) { + ctx.abortWith(Response.status(Response.Status.UNAUTHORIZED).build()); + } + } + + // Method to extract token from the request headers + private String extractToken(ContainerRequestContext ctx) { + // Extract the "Authorization" header + String authorizationHeader = ctx.getHeaderString(HttpHeaders.AUTHORIZATION); + if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) { + return authorizationHeader.substring(7); // Extract the token after "Bearer " + } + return null; // No token found + } + + private boolean isValidToken(String token) { + // You can implement your own validation logic here (e.g., checking against a + // database) + return USERS.containsKey(token); // Just an example where token is checked against a map + } + + private static Map<String, String> loadUsers() { + // Implémenter le chargement depuis un fichier (ex. users.properties) + return Map.of( + "valid-token-1", "User1", + "valid-token-2", "User2"); + } +} diff --git a/src/main/java/fil/sr2/flopbox/FTPResource.java b/src/main/java/fil/sr2/flopbox/FTPResource.java new file mode 100644 index 0000000000000000000000000000000000000000..521c49111feeac211c20b4afaf10a81b0e8bd620 --- /dev/null +++ b/src/main/java/fil/sr2/flopbox/FTPResource.java @@ -0,0 +1,211 @@ +package fil.sr2.flopbox; + +import jakarta.ws.rs.*; +import jakarta.ws.rs.core.*; +import java.io.IOException; +import java.io.InputStream; +import java.net.SocketException; +import java.net.URI; +import java.util.List; + +import org.apache.commons.net.ftp.FTP; +import org.apache.commons.net.ftp.FTPClient; + +@Path("/ftps") +public class FTPResource { + + // GET /ftps - liste des serveurs FTP enregistrés + @GET + @Produces(MediaType.APPLICATION_JSON) + public Response listFTPServers() { + List<FTPServerConfig> servers = FTPServerRepository.getInstance().getAllServers(); + return Response.ok(servers).build(); + } + + // POST /ftps - enregistre un nouveau serveur FTP + @POST + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Response addFTPServer(FTPServerConfig config, @Context UriInfo uriInfo) { + boolean created = FTPServerRepository.getInstance().addServer(config); + if (!created) { + return Response.status(Response.Status.CONFLICT) + .entity("Alias déjà utilisé").build(); + } + UriBuilder builder = uriInfo.getAbsolutePathBuilder(); + builder.path(config.getAlias()); + return Response.created(builder.build()).entity(config).build(); + } + + // GET /ftps/{alias}/{path} - récupère le contenu d'un répertoire ou d'un + // fichier sur le serveur FTP + @GET + @Path("/{alias}/{path: .+}") + @Produces(MediaType.APPLICATION_JSON) + public Response getFTPContent( + @PathParam("alias") String alias, + @PathParam("path") String path, + @HeaderParam("X-FTP-User") String ftpUser, + @HeaderParam("X-FTP-Pass") String ftpPass) { + FTPServerConfig config = FTPServerRepository.getInstance().getServer(alias); + if (config == null) { + return Response.status(Response.Status.NOT_FOUND) + .entity("Alias de serveur FTP non trouvé").build(); + } + FTPClient ftpClient = new FTPClient(); + try { + ftpClient.connect(config.getHost(), config.getPort()); + boolean login = ftpClient.login(ftpUser, ftpPass); + if (!login) { + return Response.status(Response.Status.UNAUTHORIZED) + .entity("Échec de l'authentification FTP").build(); + } + // Pour cet exemple, on se place dans le répertoire demandé et on renvoie la + // liste des fichiers. + ftpClient.changeWorkingDirectory(path); + String[] files = ftpClient.listNames(); + ftpClient.logout(); + ftpClient.disconnect(); + return Response.ok(files).build(); + } catch (IOException ex) { + return Response.status(Response.Status.INTERNAL_SERVER_ERROR) + .entity("Erreur FTP : " + ex.getMessage()).build(); + } + } + + @DELETE + @Path("/{alias}") + public Response removeFTPServer(@PathParam("alias") String alias) { + boolean removed = FTPServerRepository.getInstance().removeServer(alias); + return removed ? Response.noContent().build() : Response.status(Response.Status.NOT_FOUND).build(); + } + + @PUT + @Path("/{alias}") + @Consumes(MediaType.APPLICATION_JSON) + public Response updateFTPServer(@PathParam("alias") String alias, FTPServerConfig newConfig) { + boolean updated = FTPServerRepository.getInstance().updateServer(alias, newConfig); + return updated ? Response.ok(newConfig).build() : Response.status(Response.Status.NOT_FOUND).build(); + } + + @GET + @Path("/{alias}/{path: .+}/download") + @Produces(MediaType.APPLICATION_OCTET_STREAM) + public Response downloadFile( + @PathParam("alias") String alias, + @PathParam("path") String path, + @HeaderParam("X-FTP-User") String user, + @HeaderParam("X-FTP-Pass") String pass) { + + FTPServerConfig config = FTPServerRepository.getInstance().getServer(alias); + FTPClient ftp = new FTPClient(); + try { + ftp.connect(config.getHost(), config.getPort()); + ftp.login(user, pass); + InputStream is = ftp.retrieveFileStream(path); + return Response.ok(is) + .header("Content-Disposition", "attachment; filename=\"" + getFileName(path) + "\"") + .build(); + } catch (IOException e) { + return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build(); + } finally { + try { + if (ftp.isConnected()) { + ftp.logout(); + ftp.disconnect(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + @PUT + @Path("/{alias}/{path: .+}/upload") + @Consumes(MediaType.APPLICATION_OCTET_STREAM) + public Response uploadFile( + @PathParam("alias") String alias, + @PathParam("path") String path, + @HeaderParam("X-FTP-User") String user, + @HeaderParam("X-FTP-Pass") String pass, + InputStream fileStream) { + + FTPServerConfig config = FTPServerRepository.getInstance().getServer(alias); + FTPClient ftp = new FTPClient(); + try { + ftp.connect(config.getHost(), config.getPort()); + ftp.login(user, pass); + boolean success = ftp.storeFile(path, fileStream); + return success ? Response.ok().build() : Response.status(Response.Status.INTERNAL_SERVER_ERROR).build(); + } catch (IOException e) { + return Response.serverError().entity(e.getMessage()).build(); + } finally { + try { + if (ftp.isConnected()) { + ftp.logout(); + ftp.disconnect(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + @POST + @Path("/{alias}/{path: .+}/mkdir") + public Response createDirectory( + @PathParam("alias") String alias, + @PathParam("path") String path, + @HeaderParam("X-FTP-User") String user, + @HeaderParam("X-FTP-Pass") String pass) throws SocketException, IOException { + + FTPClient ftp = new FTPClient(); + try { + FTPServerConfig config = FTPServerRepository.getInstance().getServer(alias); + ftp.connect(config.getHost(), config.getPort()); + ftp.login(user, pass); + return ftp.makeDirectory(path) ? Response.created(URI.create(path)).build() + : Response.status(Response.Status.BAD_REQUEST).build(); + } finally { + try { + if (ftp.isConnected()) { + ftp.logout(); + ftp.disconnect(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + @DELETE + @Path("/{alias}/{path: .+}/rmdir") + public Response deleteDirectory( + @PathParam("alias") String alias, + @PathParam("path") String path, + @HeaderParam("X-FTP-User") String user, + @HeaderParam("X-FTP-Pass") String pass) throws SocketException, IOException { + + FTPClient ftp = new FTPClient(); + try { + FTPServerConfig config = FTPServerRepository.getInstance().getServer(alias); + ftp.connect(config.getHost(), config.getPort()); + ftp.login(user, pass); + return ftp.removeDirectory(path) ? Response.noContent().build() + : Response.status(Response.Status.NOT_FOUND).build(); + } finally { + try { + if (ftp.isConnected()) { + ftp.logout(); + ftp.disconnect(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + private String getFileName(String path) { + return path.substring(path.lastIndexOf('/') + 1); + } +} diff --git a/src/main/java/fil/sr2/flopbox/FTPServerConfig.java b/src/main/java/fil/sr2/flopbox/FTPServerConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..5a1ea89e15748bd8884289d800d7fa0d01219f30 --- /dev/null +++ b/src/main/java/fil/sr2/flopbox/FTPServerConfig.java @@ -0,0 +1,41 @@ +package fil.sr2.flopbox; + +public class FTPServerConfig { + private String alias; + private String host; + private int port; + + public FTPServerConfig() { + } + + public FTPServerConfig(String alias, String host, int port) { + this.alias = alias; + this.host = host; + this.port = port; + } + + // Getters et setters + public String getAlias() { + return alias; + } + + public void setAlias(String alias) { + this.alias = alias; + } + + public String getHost() { + return host; + } + + public void setHost(String host) { + this.host = host; + } + + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } +} diff --git a/src/main/java/fil/sr2/flopbox/FTPServerRepository.java b/src/main/java/fil/sr2/flopbox/FTPServerRepository.java new file mode 100644 index 0000000000000000000000000000000000000000..afe3c0654565d4768129390182904b7b074820bd --- /dev/null +++ b/src/main/java/fil/sr2/flopbox/FTPServerRepository.java @@ -0,0 +1,42 @@ +package fil.sr2.flopbox; + +import java.util.*; + +public class FTPServerRepository { + private static FTPServerRepository instance = new FTPServerRepository(); + private Map<String, FTPServerConfig> serverMap = new HashMap<>(); + + private FTPServerRepository() { + } + + public static FTPServerRepository getInstance() { + return instance; + } + + public List<FTPServerConfig> getAllServers() { + return new ArrayList<>(serverMap.values()); + } + + public FTPServerConfig getServer(String alias) { + return serverMap.get(alias); + } + + public boolean addServer(FTPServerConfig config) { + if (serverMap.containsKey(config.getAlias())) { + return false; + } + serverMap.put(config.getAlias(), config); + return true; + } + + public boolean removeServer(String alias) { + return serverMap.remove(alias) != null; + } + + public boolean updateServer(String alias, FTPServerConfig newConfig) { + if (!serverMap.containsKey(alias)) + return false; + serverMap.put(alias, newConfig); + return true; + } +} diff --git a/src/main/java/fil/sr2/flopbox/Main.java b/src/main/java/fil/sr2/flopbox/Main.java index ecede9fa28b61bbc15b373b7fc920f6ee0ac1e8e..4d067676d357a11ab04017ea1f7ddc07843a0632 100644 --- a/src/main/java/fil/sr2/flopbox/Main.java +++ b/src/main/java/fil/sr2/flopbox/Main.java @@ -4,41 +4,26 @@ import org.glassfish.grizzly.http.server.HttpServer; import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory; import org.glassfish.jersey.server.ResourceConfig; -import java.io.IOException; import java.net.URI; -/** - * Main class. - * - */ public class Main { - // Base URI the Grizzly HTTP server will listen on public static final String BASE_URI = "http://localhost:8080/"; - /** - * Starts Grizzly HTTP server exposing JAX-RS resources defined in this application. - * @return Grizzly HTTP server. - */ public static HttpServer startServer() { - // create a resource config that scans for JAX-RS resources and providers - // in fil.sr2.flopbox package - final ResourceConfig rc = new ResourceConfig().packages("fil.sr2.flopbox"); - - // create and start a new instance of grizzly http server - // exposing the Jersey application at BASE_URI + // On configure Jersey pour scanner le package "com.flopbox" (ressources, + // filtres, etc.) + final ResourceConfig rc = new ResourceConfig().packages("fil.sr2.flopbox") + .register(org.glassfish.jersey.jackson.JacksonFeature.class); return GrizzlyHttpServerFactory.createHttpServer(URI.create(BASE_URI), rc); } - /** - * Main method. - * @param args - * @throws IOException - */ - public static void main(String[] args) throws IOException { + public static void main(String[] args) { final HttpServer server = startServer(); - System.out.println(String.format("Jersey app started with endpoints available at " - + "%s%nHit Ctrl-C to stop it...", BASE_URI)); - System.in.read(); - server.stop(); + System.out.println("FlopBox API démarrée sur " + BASE_URI); + try { + Thread.currentThread().join(); + } catch (InterruptedException ex) { + System.err.println("Serveur interrompu : " + ex.getMessage()); + } } } diff --git a/src/test/java/fil/sr2/flopbox/MyResourceTest.java b/src/test/java/fil/sr2/flopbox/MyResourceTest.javaTEMPO similarity index 100% rename from src/test/java/fil/sr2/flopbox/MyResourceTest.java rename to src/test/java/fil/sr2/flopbox/MyResourceTest.javaTEMPO