From cf4a97ac2c277dc43a3e46cd2b21acbaba897985 Mon Sep 17 00:00:00 2001 From: RPJosh Date: Sun, 10 Apr 2022 16:00:03 +0200 Subject: [PATCH] Dynamic basic auth based on response status and comments translations - the method requires an additional parameter if the dynamic basic auth should be supported and the user gets a prompt to enter the credentials --- .../de/rpjosh/installer/InstallConfig.java | 10 +++- .../java/de/rpjosh/installer/Installer.java | 57 ++++++++++++++----- .../de/rpjosh/installer/RunInConsole.java | 55 +++++++++--------- .../de-rpjosh-installer_de.properties | 6 +- .../de-rpjosh-installer_en.properties | 6 +- 5 files changed, 90 insertions(+), 44 deletions(-) diff --git a/src/main/java/de/rpjosh/installer/InstallConfig.java b/src/main/java/de/rpjosh/installer/InstallConfig.java index 0fc6c37..9e3d2d8 100644 --- a/src/main/java/de/rpjosh/installer/InstallConfig.java +++ b/src/main/java/de/rpjosh/installer/InstallConfig.java @@ -62,6 +62,7 @@ public class InstallConfig { protected String urlEnding = ""; protected char[] authUsername = null; protected char[] authPassword = null; + protected boolean allowAskForBasicAuth = false; private boolean createDesktopEntry = false; private String desktopWindowsICO = ""; @@ -184,11 +185,14 @@ public class InstallConfig { * @param url the URL * @param basicAuthUser [ the user for the basic auth ] * @param basicAuthPassword [ the passwort for the basic auth ] + * @param askForBasicAuth when no basic auth credentials are given and the request gets a 401 response ask the user for credentials at the command line */ - public void setDownloadURLForProgramm(String url, char[] basicAuthUser, char[] basicAuthPassword) { + public void setDownloadURLForProgramm(String url, char[] basicAuthUser, char[] basicAuthPassword, boolean askForBasicAuth) { this.downloadURL = url; this.authUsername = basicAuthUser; this.authPassword = basicAuthPassword; + this.allowAskForBasicAuth = askForBasicAuth; + offline = false; } @@ -199,14 +203,16 @@ public class InstallConfig { * @param url die URL (without file extension) * @param basicAuthUser [ the user for the basic auth ] * @param basicAuthPassword [ the passwort for the basic auth ] + * @param askForBasicAuth when no basic auth credentials are given and the request gets a 401 response ask the user for credentials at the command line * @param end die file ending of the file (.jar) */ - public void setDownloadURLForProgramm(String url, char[] basicAuthUser, char[] basicAuthPassword, String end) { + public void setDownloadURLForProgramm(String url, char[] basicAuthUser, char[] basicAuthPassword, boolean askForBasicAuth, String end) { this.downloadURL = url; this.authUsername = basicAuthUser; this.authPassword = basicAuthPassword; this.urlEnding = end; this.addVersion = true; + this.allowAskForBasicAuth = askForBasicAuth; offline = false; } diff --git a/src/main/java/de/rpjosh/installer/Installer.java b/src/main/java/de/rpjosh/installer/Installer.java index 33d8225..436da74 100644 --- a/src/main/java/de/rpjosh/installer/Installer.java +++ b/src/main/java/de/rpjosh/installer/Installer.java @@ -14,7 +14,6 @@ import java.net.URL; import java.nio.channels.Channels; import java.nio.channels.ReadableByteChannel; import java.text.DecimalFormat; -import java.util.ArrayList; import java.util.Arrays; import java.util.Base64; import java.util.List; @@ -94,7 +93,7 @@ public class Installer { if (conf.getIsUser() && InstallConfig.getOsType() != OSType.WINDOWS) { System.out.println("userInstallation_notAvailable"); error = -10; } - System.out.println("\n" + Tr.get("installation_start", conf.getApplicationNameShort(), conf.getVersion()) + "\n"); + System.out.println(Tr.get("installation_start", conf.getApplicationNameShort(), conf.getVersion()) + "\n"); // all running instances will be killed this.killRunningInstances(); @@ -114,7 +113,7 @@ public class Installer { if (!conf.getOffline()) { System.out.print(Tr.get("installation_download") + ": "); - jarFile = this.downloadFile(conf.downloadURL, conf.addVersion, conf.urlEnding); + jarFile = this.downloadFile(conf.downloadURL, conf.addVersion, conf.urlEnding, conf.getQuiet() ? false : conf.allowAskForBasicAuth); if (error < 0) return; System.out.println("\r" + Tr.get("installation_download_success") + " "); } else { @@ -906,14 +905,16 @@ public class Installer { } /** - * Downloads a file from an Webserver (with bsic auth support -> set in config) + * Downloads a file from an Webserver (with basic auth support -> set in config) + * + * @param url URL + * @param addVersion if the architecture and BS should be added to the given URL -> windows_x64 | linux_arm32 + * @param end the file ending (the URL will be set without an file ending) + * @param ascForBasicAuth when no basic auth credentials are given and the request gets a 401 response ask the user for credentials at the command line * - * @param url URL - * @param addVersion if the architecture and bs should be added to the given URL -> windows_x64 | linux_arm32 - * @param end the file ending (the URL will be set without an file ending) * @return the path of the downloaded file (when an error occurred: null + logger.error) */ - private String downloadFile (String url2, boolean addVersion, String end) { + private String downloadFile (String url2, boolean addVersion, String end, boolean askForAuth) { String serverURL = conf.downloadURL; @@ -922,24 +923,54 @@ public class Installer { try { URL url = new URL(serverURL); HttpURLConnection con = (HttpURLConnection) url.openConnection(); - if (conf.authUsername != null && conf.authPassword != null) { + + // check if basic auth is required + if (con.getResponseCode() == HttpURLConnection.HTTP_UNAUTHORIZED) { + if (conf.authUsername == null || conf.authPassword == null) { + if (!askForAuth) { logger.log("e", "Baisc authentication required for downloading the file \"" + serverURL + "\"", ""); System.exit(-1); } + + char[] username = conf.authUsername; + char[] password = conf.authPassword; + + System.out.println("\n" + Tr.get("basicAuthRequired")); + + if (System.console() == null) { System.out.println(Tr.get("noConsole")); System.exit(-1); } + if (username == null) { + System.out.print(Tr.get("username") + ": "); + username = System.console().readLine().strip().toCharArray(); + } + if (password == null) { + System.out.print(Tr.get("password") + ": "); + password = System.console().readPassword(); + } + System.out.println(); + conf.authUsername = username; + conf.authPassword = password; + } + + // add Basic-Auth String auth = new String(conf.authUsername) + ":" + new String(conf.authPassword); byte[] authEncBytes = Base64.getEncoder().encode(auth.getBytes()); String authHeaderValue = "Basic " + new String(authEncBytes); + con = (HttpURLConnection) url.openConnection(); con.setRequestProperty("Authorization", authHeaderValue); con.setRequestProperty("X-Requested-With", "XMLHttpRequest"); - } + } + if (con.getResponseCode() == HttpURLConnection.HTTP_UNAUTHORIZED) { + logger.log("e", "Authentication failed for url \"" + serverURL + "\"", ""); + System.exit(-1); + } try { File download = File.createTempFile("Download-Installation", ".jar"); - // für eine Statusanzeige wird zunächst die Dateigröße der Datei ermittelt (wird in Bytes ausgegeben) + // for a download status the size of the downloadable file is determined (in Bytes) double lenght = con.getContentLength(); if (lenght < 100 * 1024) { throw new Exception ("Probably not a file (lenght to short)"); } - // diese wird nun in Megabyte angegeben, sowie auf 2 Stellen nach dem Komma (Es wird die bereits heruntergeladene Dateigröße im "Binärformat" angegeben + // round to megabytes and two decimal points lenght = Math.round(lenght / 1048576 * 100) / 100.0; - // es muss nun parallel die bereits heruntergeladene Dateigröße ermittelt werden + // in parallel the already downloaded file size has to be determined ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(); double lenghtTmp = lenght; Future future = scheduler.scheduleWithFixedDelay(() -> { diff --git a/src/main/java/de/rpjosh/installer/RunInConsole.java b/src/main/java/de/rpjosh/installer/RunInConsole.java index 7bd82a7..232e399 100644 --- a/src/main/java/de/rpjosh/installer/RunInConsole.java +++ b/src/main/java/de/rpjosh/installer/RunInConsole.java @@ -12,8 +12,9 @@ public class RunInConsole { /** - * Öffnet das Programm in der Konsole, falls dieses noch nicht in solch einer ausgeführt wird - * @param keepOpen Ob die Konsole nach dem Durchlauf des Programms geschlossen werden soll + * Opens the program inside a console if not already run inside a console window + * + * @param keepOpen the console will stay opened after the program was closed */ public static void start(boolean keepOpen) { start (keepOpen, null, false, false); @@ -21,20 +22,22 @@ public class RunInConsole { /** - * Öffnet das Programm in der Konsole, falls dieses noch nicht in solch einer ausgeführt wird - * @param args Mit welchen Parametern die Main-Methode beliefert werden soll - * @param keepOpen Ob die Konsole nach dem durchlauf des Programms geöffnet bleiben soll + * Opens the program inside a console if not already run inside a console window + * + * @param args the command line options for the main method when calling the program inside the console again + * @param keepOpen the console will stay opened after the program finishes */ public static void start(String[] args, boolean keepOpen) { start(keepOpen, args, false, false); } /** - * Öffnet das Programm in der Konsole - * @param args Mit welchen Parametern die Main-Methode beliefert werden soll - * @param keepOpen Ob die Konsole nach dem durchlauf des Programms geöffnet bleiben soll - * @param forceRestart Ob ein Neustart gemacht werden soll, wenn das Programm bereits in der Konsole läuft - * @param asAdmin Ob das Programm mit Administratorprivelegien gestartet werden soll (Powershell muss installiert sein) + * Opens the program inside a console if not already run inside a console window + * + * @param args the command line options for the main method when calling the program inside the console again + * @param keepOpen the console will stay opened after the program finishes + * @param forceRestart even restart the program when already running inside a console + * @param asAdmin start the console with administrator privileges (on Windows Powershell is required) */ public static void start(String[] args, boolean keepOpen, boolean forceRestart, boolean asAdmin) { start(keepOpen, args, forceRestart, asAdmin); @@ -45,9 +48,9 @@ public class RunInConsole { String executableName = getExecutableName(); - // wird vermutlich in einer IDE ausgeführt + // probably executed inside IDE if (executableName == null) return; - // wird bereits in der Konsole ausgeführt + // is already executed in console if (System.console() != null && !forceRestart) return; startExecutableInConsole(executableName, keepOpen, asAdmin, args); @@ -57,17 +60,17 @@ public class RunInConsole { /** - * Öffnen ein Konsolenfenster und startet in diesem die Jar-Datei + * Opens the console windows and starts the jar file * - * @param executableName Der Name der Jar-Datei (ohne Pfad -> relativ) - * @param stayOpenAfterEnd Ob das Konsolenfenster nach dem vollständigen durchlauf der Jar-Datei geschlossen werden soll - * @param asAdmin Ob das Programm mit Administratorprivelegien gestartet werden soll (Powershell muss installiert sein) + * @param executableName the name of the jar file (without the path -> relativ) + * @param stayOpenAfterEnd keep the console windows opened after the run of the jar file + * @param asAdmin start the console with administrator privileges (on Windows Powershell is required) */ private static void startExecutableInConsole(String executableName, final boolean keepOpen, final boolean asAdmin, String[] args) { String command = null; - // es müssen nun noch die Parameter ermittelt werden + // determine the parameters String strArgs = ""; for (String currentArg: args) { strArgs += "\"" + currentArg + "\" "; @@ -107,14 +110,12 @@ public class RunInConsole { if (output != null && !output.equals("")) { terminal = currentTerminal[0]; terminalCommand = currentTerminal[1]; break; } } - if (terminal == null) break; if (!asAdmin) { if (keepOpen) new ProcessBuilder("bash", "-c", terminal + " " + terminalCommand + " /bin/sh -c 'java -jar \"" + executableName + "\" " + strArgs + "; exec bash'").start(); else new ProcessBuilder("bash", "-c", terminal + " " + terminalCommand + " /bin/sh -c 'java -jar \"" + executableName + "\" " + strArgs + "'").start(); } else { - if (keepOpen) new ProcessBuilder("bash", "-c", terminal + " " + terminalCommand + " sudo /bin/sh -c 'java -jar \"" + executableName + "\" " + strArgs + "; exec bash'").start(); else new ProcessBuilder("bash", "-c", terminal + " " + terminalCommand + " sudo /bin/sh -c 'java -jar \"" + executableName + "\" " + strArgs + "'").start(); } @@ -133,7 +134,7 @@ public class RunInConsole { /** - * @return Der Name der Jar-Datei (BeMa_installer.jar) + * @return the name of the jar file (MyInstaller.jar) */ public static String getExecutableName() { @@ -141,11 +142,11 @@ public class RunInConsole { final CodeSource codeSource = RunInConsole.class.getProtectionDomain().getCodeSource(); if (codeSource == null) { - // es wird nichts geloggt + // do nothing } else { String path = codeSource.getLocation().getPath(); if (path == null || path.isEmpty()) { - // es wird nichts geloggt; + // do nothing } else { executableNameFromClass = new File(path).getName(); } @@ -165,18 +166,18 @@ public class RunInConsole { /** - * Gibt zurück, ob es sich um eine .jar Datei handelt, und ob diese existiert - * @param name Name der Jar Datei - * @return Ob es sich um eine Jar-Datei handelt + * Checks if the given file path is valid and if its a jar file + * + * @param the name of the jar file + * @return the file path is valid and a jar file */ private static boolean isJarFile(final String name) { if (name == null || !name.toLowerCase().endsWith(".jar")) return false; - // überprüfe, ob diese existiert + // checks if file exists final File file = new File(name); return file.exists() && file.isFile(); } - } diff --git a/src/main/resources/translation/de-rpjosh-installer_de.properties b/src/main/resources/translation/de-rpjosh-installer_de.properties index 4e786c2..c45f55a 100644 --- a/src/main/resources/translation/de-rpjosh-installer_de.properties +++ b/src/main/resources/translation/de-rpjosh-installer_de.properties @@ -30,4 +30,8 @@ notAuthorized=Keine Berechtigung created=erstellt failed=fehlgeschlagen errorMessage=Fehlermeldung -successful=erfolgreich \ No newline at end of file +successful=erfolgreich + +basicAuthRequired=Zum herunterladen des Programms ist eine Authentifizierung erforderlich (HTTP-Code 401) +username=Benutzername +password=Passwort \ No newline at end of file diff --git a/src/main/resources/translation/de-rpjosh-installer_en.properties b/src/main/resources/translation/de-rpjosh-installer_en.properties index 43b4dc3..2f34abd 100644 --- a/src/main/resources/translation/de-rpjosh-installer_en.properties +++ b/src/main/resources/translation/de-rpjosh-installer_en.properties @@ -30,4 +30,8 @@ notAuthorized=No authorization created=created failed=failed errorMessage=Error Message -successful=successful \ No newline at end of file +successful=successful + +basicAuthRequired=For downloading the program a authentication is required (HTTP-Code 401) +username=Username +password=Password \ No newline at end of file