├── settings.gradle ├── icon.png ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── .gitignore ├── src ├── test │ └── java │ │ └── io │ │ └── ryoung │ │ └── bitwarden │ │ └── BitwardenPluginTest.java └── main │ └── java │ └── io │ └── ryoung │ └── bitwarden │ ├── BitwardenConfig.java │ ├── Credential.java │ ├── BitwardenPlugin.java │ ├── CommandRunner.java │ └── CredentialsManager.java ├── runelite-plugin.properties ├── LICENSE ├── README.md ├── gradlew.bat └── gradlew /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'bitwarden' 2 | -------------------------------------------------------------------------------- /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raiyni/bitwarden-plugin/HEAD/icon.png -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raiyni/bitwarden-plugin/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | build 3 | .idea/ 4 | .project 5 | .settings/ 6 | .classpath 7 | nbactions.xml 8 | nb-configuration.xml 9 | nbproject/ -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /src/test/java/io/ryoung/bitwarden/BitwardenPluginTest.java: -------------------------------------------------------------------------------- 1 | package io.ryoung.bitwarden; 2 | 3 | import net.runelite.client.RuneLite; 4 | import net.runelite.client.externalplugins.ExternalPluginManager; 5 | 6 | public class BitwardenPluginTest 7 | { 8 | public static void main(String[] args) throws Exception 9 | { 10 | ExternalPluginManager.loadBuiltin(BitwardenPlugin.class); 11 | RuneLite.main(args); 12 | } 13 | } -------------------------------------------------------------------------------- /runelite-plugin.properties: -------------------------------------------------------------------------------- 1 | displayName=Bitwarden Plugin 2 | author=raiyni 3 | support=https://github.com/raiyni/bitwarden-plugin 4 | description=Plugin that interfaces with the Bitwarden CLI to retrieve RuneScape passwords.
Visit support url for usage 5 | warning=You are required to use a CLI application in conjunction with this plugin.
Read the support information and additional documentation for usage. 6 | tags=password,account,login,bw 7 | plugins=io.ryoung.bitwarden.BitwardenPlugin -------------------------------------------------------------------------------- /src/main/java/io/ryoung/bitwarden/BitwardenConfig.java: -------------------------------------------------------------------------------- 1 | package io.ryoung.bitwarden; 2 | 3 | import net.runelite.client.config.Config; 4 | import net.runelite.client.config.ConfigGroup; 5 | import net.runelite.client.config.ConfigItem; 6 | 7 | @ConfigGroup("bitwarden") 8 | public interface BitwardenConfig extends Config 9 | { 10 | @ConfigItem( 11 | keyName = "clearKeyOnLogin", 12 | name = "Clear Session Key on Login", 13 | description = "Clear Session Key on Login" 14 | ) 15 | default boolean clearKeyOnLogin() 16 | { 17 | return false; 18 | } 19 | 20 | @ConfigItem( 21 | keyName = "bwLocation", 22 | name = "bw location", 23 | description = "Location of bw executable" 24 | ) 25 | default String bwLocation() 26 | { 27 | return "bw"; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 2-Clause License 2 | 3 | Copyright (c) 2020, Ron Young 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /src/main/java/io/ryoung/bitwarden/Credential.java: -------------------------------------------------------------------------------- 1 | package io.ryoung.bitwarden; 2 | 3 | import com.google.gson.JsonDeserializationContext; 4 | import com.google.gson.JsonDeserializer; 5 | import com.google.gson.JsonElement; 6 | import com.google.gson.JsonObject; 7 | import com.google.gson.JsonParseException; 8 | import com.google.gson.reflect.TypeToken; 9 | import java.lang.reflect.Type; 10 | import java.util.List; 11 | import java.util.Optional; 12 | import lombok.Getter; 13 | 14 | final class Credential 15 | { 16 | @Getter 17 | private final String username; 18 | 19 | @Getter 20 | private final char[] password; 21 | 22 | static final Type TYPE = new TypeToken>() 23 | { 24 | }.getType(); 25 | 26 | private Credential(JsonObject jsonObject) 27 | { 28 | this.username = jsonObject.get("username").getAsString(); 29 | this.password = jsonObject.get("password").getAsString().toCharArray(); 30 | } 31 | 32 | static final class Deserializer implements JsonDeserializer 33 | { 34 | @Override 35 | public Credential deserialize(JsonElement json, Type typeOfT, 36 | JsonDeserializationContext context) throws JsonParseException 37 | { 38 | JsonObject jsonObject = json.getAsJsonObject(); 39 | 40 | return Optional.ofNullable(jsonObject.get("login")) 41 | .map(JsonElement::getAsJsonObject) 42 | .map(Credential::new) 43 | .orElse(null); 44 | } 45 | } 46 | } 47 | 48 | 49 | -------------------------------------------------------------------------------- /src/main/java/io/ryoung/bitwarden/BitwardenPlugin.java: -------------------------------------------------------------------------------- 1 | package io.ryoung.bitwarden; 2 | 3 | import com.google.common.base.Strings; 4 | import com.google.inject.Provides; 5 | import javax.inject.Inject; 6 | import lombok.extern.slf4j.Slf4j; 7 | import net.runelite.api.Client; 8 | import net.runelite.api.GameState; 9 | import net.runelite.api.events.GameStateChanged; 10 | import net.runelite.api.events.UsernameChanged; 11 | import net.runelite.client.config.ConfigManager; 12 | import net.runelite.client.eventbus.Subscribe; 13 | import net.runelite.client.plugins.Plugin; 14 | import net.runelite.client.plugins.PluginDescriptor; 15 | 16 | 17 | @Slf4j 18 | @PluginDescriptor( 19 | name = "Bitwarden" 20 | ) 21 | public class BitwardenPlugin extends Plugin 22 | { 23 | @Inject 24 | private Client client; 25 | 26 | @Inject 27 | private BitwardenConfig config; 28 | 29 | @Inject 30 | private CredentialsManager credentialsManager; 31 | 32 | @Subscribe 33 | public void onGameStateChanged(GameStateChanged event) 34 | { 35 | if (event.getGameState() == GameState.LOGGED_IN) 36 | { 37 | credentialsManager.clearEntries(); 38 | if (config.clearKeyOnLogin()) 39 | { 40 | credentialsManager.clearSessionKey(); 41 | } 42 | } 43 | } 44 | 45 | @Subscribe 46 | public void onUsernameChanged(UsernameChanged event) 47 | { 48 | if (client.getGameState() != GameState.LOGIN_SCREEN) 49 | { 50 | return; 51 | } 52 | 53 | credentialsManager.injectPassword(); 54 | } 55 | 56 | @Override 57 | protected void startUp() throws Exception 58 | { 59 | String envKey = System.getenv("BW_SESSION"); 60 | if (!Strings.isNullOrEmpty(envKey)) 61 | { 62 | credentialsManager.setSessionKey(envKey.toCharArray()); 63 | } 64 | credentialsManager.injectPassword(); 65 | } 66 | 67 | @Override 68 | protected void shutDown() throws Exception 69 | { 70 | credentialsManager.reset(); 71 | } 72 | 73 | @Provides 74 | BitwardenConfig provideConfig(ConfigManager configManager) 75 | { 76 | return configManager.getConfig(BitwardenConfig.class); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/io/ryoung/bitwarden/CommandRunner.java: -------------------------------------------------------------------------------- 1 | package io.ryoung.bitwarden; 2 | 3 | import com.google.common.base.CharMatcher; 4 | import com.google.common.io.CharStreams; 5 | import java.io.IOException; 6 | import java.io.InputStream; 7 | import java.io.InputStreamReader; 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | import java.util.concurrent.CompletableFuture; 11 | import java.util.concurrent.ExecutionException; 12 | import java.util.function.Consumer; 13 | import net.runelite.client.util.OSType; 14 | 15 | class CommandRunner extends Thread 16 | { 17 | 18 | private static final CharMatcher CHAR_MATCHER = CharMatcher.inRange('0', '9') 19 | .or(CharMatcher.inRange('a', 'z')) 20 | .or(CharMatcher.inRange('A', 'Z')) 21 | .or(CharMatcher.anyOf("=+/")); 22 | 23 | CommandRunner(String bw, char[] sessionKey, Consumer consumer) 24 | { 25 | super(() -> { 26 | try 27 | { 28 | ProcessBuilder pb = buildCommand(bw, new String(sessionKey)); 29 | Process p = pb.start(); 30 | 31 | CompletableFuture sout = readOutStream(p.getInputStream()); 32 | CompletableFuture serror = readOutStream(p.getErrorStream()); 33 | CompletableFuture result = sout.thenCombine(serror, (stdout, stderr) -> 34 | { 35 | if (!stdout.startsWith("[")) 36 | { 37 | return stdout + stderr; 38 | } 39 | 40 | return stdout; 41 | }); 42 | 43 | p.waitFor(); 44 | consumer.accept(result.get()); 45 | } 46 | catch (IOException | InterruptedException | ExecutionException e) 47 | { 48 | // do nothing 49 | } 50 | }); 51 | } 52 | 53 | static CompletableFuture readOutStream(InputStream is) 54 | { 55 | return CompletableFuture.supplyAsync(() -> { 56 | String s = ""; 57 | try 58 | { 59 | s = CharStreams.toString(new InputStreamReader(is)); 60 | } 61 | catch (IOException e) 62 | { 63 | // do nothing 64 | } 65 | 66 | return s; 67 | }); 68 | } 69 | 70 | private static ProcessBuilder buildCommand(String bw, String sessionKey) 71 | { 72 | String filteredKey = CHAR_MATCHER.retainFrom(sessionKey); 73 | List params = new ArrayList<>(); 74 | if (OSType.getOSType() == OSType.Windows) 75 | { 76 | params.add("cmd"); 77 | params.add("/c"); 78 | } 79 | else 80 | { 81 | params.add("bash"); 82 | params.add("-c"); 83 | } 84 | 85 | String redirect = OSType.getOSType() == OSType.Windows ? " < NUL" : " < /dev/null"; 86 | params.add(bw + " list items --search runescape.com --session \"" + filteredKey + "\"" + redirect); 87 | 88 | return new ProcessBuilder(params); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Bitwarden 2 | Plugin that interfaces with the Bitwarden CLI to retrieve RuneScape passwords. 3 | 4 | This plugin requires: 5 | 6 | * You have the [Bitwarden CLI](https://bitwarden.com/help/article/cli/) installed 7 | * `bw` is accessible in your PATH 8 | * You are comfortable using a command line interface 9 | * Your accounts are each under a website entry of `runescape.com` 10 | 11 | It's recommended you walk through the documentation for [Bitwarden CLI](https://bitwarden.com/help/article/cli/), so you are aware of the features of the application and the differences between logout and lock. 12 | 13 | **As long as you leave the CLI window open with the session key visible, or a browser window with Bitwarden logged in, someone can still access your account on your PC.** 14 | 15 | No login information is saved between sessions. 16 | 17 | # Usage 18 | 19 | There are two ways to use this plugin. 20 | 21 | #### Active 22 | 23 | With this method, you have to keep your session key on hand and enter it every time the plugin starts. This would keep your information more secure if multiple people use your computer. Combine this with the `Clear Key on Login` config option to have the most security. 24 | 25 | 26 | 1. Login to your Bitwarden vault through the CLI with `bw login --raw` and copy the resulting session key. Make sure not to copy the newline following it. 27 | 1. Load RuneLite with this plugin enabled or enable this plugin while on the login screen. 28 | 1. Paste your session key into the popup asking for it and press `Ok`. 29 | * Pressing cancel will suppress the popup until your next client start or plugin toggle. 30 | 1. Once you switch to the login page, your password should fill in when it matches your username. 31 | * It may take a few seconds for your information to load depending on your internet connection. 32 | * If an error occurs, you should receive a popup indicating that. 33 | 1. If you are using the AppImage, you will need to set the path to your executable in the config for the plugin. 34 | 35 | #### Passive 36 | 37 | With this method, you can manage your vault by locking it after you close RuneLite and unlocking it before you open it. It's a mildly faster way to get into the game, but it can be less secure if you have multiple users on your computer. 38 | 39 | 1. Login to your Bitwarden vault through the CLI with `bw login --raw`. 40 | 1. Set the environment variable `BW_SESSION` to your key. 41 | * On Windows, you can use the command `setx` or create an environment variable through the system menus. 42 | * On OSX/Linux, add the variable to your `.profile` file. 43 | * On OSX you might need to set it with `launchctl setenv BW_SESSION "YOUR_SESSION_KEY_HERE"` 44 | 1. Restart RuneLite if it is open. 45 | 1. Once you switch to the login page, your password should fill in when it matches your username. 46 | * It may take a few seconds for your information to load depending on your internet connection. 47 | * If an error occurs, you should receive a popup indicating that. 48 | 1. If the session key from `BW_SESSION` seems to be invalid, you will be prompted for a new key. 49 | 1. If you are using the AppImage, you will need to set the path to your executable in the config for the plugin. 50 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 33 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 34 | 35 | @rem Find java.exe 36 | if defined JAVA_HOME goto findJavaFromJavaHome 37 | 38 | set JAVA_EXE=java.exe 39 | %JAVA_EXE% -version >NUL 2>&1 40 | if "%ERRORLEVEL%" == "0" goto init 41 | 42 | echo. 43 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 44 | echo. 45 | echo Please set the JAVA_HOME variable in your environment to match the 46 | echo location of your Java installation. 47 | 48 | goto fail 49 | 50 | :findJavaFromJavaHome 51 | set JAVA_HOME=%JAVA_HOME:"=% 52 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 53 | 54 | if exist "%JAVA_EXE%" goto init 55 | 56 | echo. 57 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 58 | echo. 59 | echo Please set the JAVA_HOME variable in your environment to match the 60 | echo location of your Java installation. 61 | 62 | goto fail 63 | 64 | :init 65 | @rem Get command-line arguments, handling Windows variants 66 | 67 | if not "%OS%" == "Windows_NT" goto win9xME_args 68 | 69 | :win9xME_args 70 | @rem Slurp the command line arguments. 71 | set CMD_LINE_ARGS= 72 | set _SKIP=2 73 | 74 | :win9xME_args_slurp 75 | if "x%~1" == "x" goto execute 76 | 77 | set CMD_LINE_ARGS=%* 78 | 79 | :execute 80 | @rem Setup the command line 81 | 82 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 83 | 84 | @rem Execute Gradle 85 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 86 | 87 | :end 88 | @rem End local scope for the variables with windows NT shell 89 | if "%ERRORLEVEL%"=="0" goto mainEnd 90 | 91 | :fail 92 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 93 | rem the _cmd.exe /c_ return code! 94 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 95 | exit /b 1 96 | 97 | :mainEnd 98 | if "%OS%"=="Windows_NT" endlocal 99 | 100 | :omega 101 | -------------------------------------------------------------------------------- /src/main/java/io/ryoung/bitwarden/CredentialsManager.java: -------------------------------------------------------------------------------- 1 | package io.ryoung.bitwarden; 2 | 3 | import com.google.gson.Gson; 4 | import com.google.gson.GsonBuilder; 5 | import com.google.gson.JsonParseException; 6 | import java.util.ArrayList; 7 | import java.util.Arrays; 8 | import java.util.List; 9 | import javax.inject.Inject; 10 | import javax.inject.Named; 11 | import javax.swing.JLabel; 12 | import javax.swing.JOptionPane; 13 | import javax.swing.JPanel; 14 | import javax.swing.JPasswordField; 15 | import javax.swing.SwingUtilities; 16 | import lombok.extern.slf4j.Slf4j; 17 | import net.runelite.api.Client; 18 | 19 | @Slf4j 20 | final class CredentialsManager 21 | { 22 | private final Gson GSON; 23 | 24 | private final Client client; 25 | private CommandRunner commandRunner = null; 26 | 27 | private char[] sessionKey = new char[0]; 28 | private List entries = new ArrayList<>(); 29 | 30 | private boolean keepTrying = true; 31 | private BitwardenConfig config; 32 | 33 | private boolean developerMode; 34 | 35 | @Inject 36 | CredentialsManager(Client client, BitwardenConfig config, @Named("developerMode") boolean developerMode, Gson gson) 37 | { 38 | this.client = client; 39 | this.config = config; 40 | this.developerMode = developerMode; 41 | this.GSON = gson.newBuilder().registerTypeAdapter(Credential.class, new Credential.Deserializer()).create(); 42 | } 43 | 44 | private void parseIssue(String result) 45 | { 46 | commandRunner = null; 47 | entries.clear(); 48 | 49 | if (developerMode) 50 | { 51 | log.error(result); 52 | } 53 | 54 | if (result.contains("Session key is invalid")) 55 | { 56 | SwingUtilities.invokeLater(() -> { 57 | JOptionPane.showMessageDialog(null, "Your session key is invalid.", 58 | "", JOptionPane.INFORMATION_MESSAGE); 59 | askForKey(); 60 | }); 61 | return; 62 | } 63 | 64 | if (result.startsWith("? Master password:")) 65 | { 66 | SwingUtilities.invokeLater(() -> JOptionPane.showMessageDialog(null, "Your vault is locked.", 67 | "", JOptionPane.INFORMATION_MESSAGE)); 68 | return; 69 | } 70 | 71 | clearSessionKey(); 72 | 73 | if (result.contains("You are not logged in")) 74 | { 75 | SwingUtilities.invokeLater(() -> { 76 | JOptionPane.showMessageDialog(null, "You are not logged into Bitwarden CLI.", 77 | "", JOptionPane.INFORMATION_MESSAGE); 78 | askForKey(); 79 | }); 80 | } 81 | else if (result.contains("mac failed")) 82 | { 83 | SwingUtilities.invokeLater(() -> { 84 | JOptionPane.showMessageDialog(null, "Error loading vault. Your session key might be wrong." + 85 | "\nTry: " + 86 | "\n1. Closing RuneLite" + 87 | "\n2. Locking your vault" + 88 | "\n3. Deleting your session key" + 89 | "\n4. Closing your terminal" + 90 | "\n5. Then unlock your vault and save your session key", 91 | "", JOptionPane.ERROR_MESSAGE); 92 | askForKey(); 93 | }); 94 | } 95 | else 96 | { 97 | SwingUtilities.invokeLater(() -> { 98 | JOptionPane.showMessageDialog(null, "Unknown error: " + result, 99 | "", JOptionPane.ERROR_MESSAGE); 100 | askForKey(); 101 | }); 102 | } 103 | } 104 | 105 | private void consumeResult(String result) 106 | { 107 | if (!result.startsWith("[")) 108 | { 109 | parseIssue(result); 110 | return; 111 | } 112 | 113 | try 114 | { 115 | entries = GSON.fromJson(result, Credential.TYPE); 116 | 117 | setPassword(); 118 | } 119 | catch (JsonParseException | NullPointerException e) 120 | { 121 | entries.clear(); 122 | 123 | SwingUtilities.invokeLater(() -> JOptionPane.showMessageDialog(null, "Error parsing credential results.", 124 | "", JOptionPane.ERROR_MESSAGE)); 125 | } 126 | finally 127 | { 128 | commandRunner = null; 129 | } 130 | } 131 | 132 | private void setPassword() 133 | { 134 | String username = client.getUsername(); 135 | for (Credential credential : entries) 136 | { 137 | if (credential != null && username.equalsIgnoreCase(credential.getUsername())) 138 | { 139 | client.setPassword(new String(credential.getPassword())); 140 | return; 141 | } 142 | } 143 | } 144 | 145 | void clearSessionKey() 146 | { 147 | this.sessionKey = new char[0]; 148 | } 149 | 150 | void setSessionKey(char[] key) 151 | { 152 | this.sessionKey = Arrays.copyOf(key, key.length); 153 | } 154 | 155 | void clearEntries() 156 | { 157 | entries.clear(); 158 | } 159 | 160 | void reset() 161 | { 162 | clearSessionKey(); 163 | entries.clear(); 164 | commandRunner = null; 165 | keepTrying = true; 166 | } 167 | 168 | void injectPassword() 169 | { 170 | if (sessionKey.length == 0) 171 | { 172 | askForKey(); 173 | } 174 | else if (commandRunner == null && entries.isEmpty()) 175 | { 176 | commandRunner = new CommandRunner(config.bwLocation(), sessionKey, this::consumeResult); 177 | commandRunner.start(); 178 | } 179 | else 180 | { 181 | setPassword(); 182 | } 183 | } 184 | 185 | private void askForKey() 186 | { 187 | if (!keepTrying) 188 | { 189 | return; 190 | } 191 | 192 | SwingUtilities.invokeLater(() -> { 193 | JPanel panel = new JPanel(); 194 | JLabel label = new JLabel("Session Key:"); 195 | JPasswordField pass = new JPasswordField(128); 196 | panel.add(label); 197 | panel.add(pass); 198 | int option = JOptionPane.showOptionDialog(null, panel, "Bitwarden", 199 | JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE, 200 | null, null, ""); 201 | if (option == 0) // pressing OK button 202 | { 203 | setSessionKey(pass.getPassword()); 204 | injectPassword(); 205 | } 206 | else 207 | { 208 | keepTrying = false; 209 | clearSessionKey(); 210 | } 211 | }); 212 | } 213 | } 214 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | # Determine the Java command to use to start the JVM. 86 | if [ -n "$JAVA_HOME" ] ; then 87 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 88 | # IBM's JDK on AIX uses strange locations for the executables 89 | JAVACMD="$JAVA_HOME/jre/sh/java" 90 | else 91 | JAVACMD="$JAVA_HOME/bin/java" 92 | fi 93 | if [ ! -x "$JAVACMD" ] ; then 94 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 95 | 96 | Please set the JAVA_HOME variable in your environment to match the 97 | location of your Java installation." 98 | fi 99 | else 100 | JAVACMD="java" 101 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 102 | 103 | Please set the JAVA_HOME variable in your environment to match the 104 | location of your Java installation." 105 | fi 106 | 107 | # Increase the maximum file descriptors if we can. 108 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 109 | MAX_FD_LIMIT=`ulimit -H -n` 110 | if [ $? -eq 0 ] ; then 111 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 112 | MAX_FD="$MAX_FD_LIMIT" 113 | fi 114 | ulimit -n $MAX_FD 115 | if [ $? -ne 0 ] ; then 116 | warn "Could not set maximum file descriptor limit: $MAX_FD" 117 | fi 118 | else 119 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 120 | fi 121 | fi 122 | 123 | # For Darwin, add options to specify how the application appears in the dock 124 | if $darwin; then 125 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 126 | fi 127 | 128 | # For Cygwin or MSYS, switch paths to Windows format before running java 129 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 130 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 131 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 132 | JAVACMD=`cygpath --unix "$JAVACMD"` 133 | 134 | # We build the pattern for arguments to be converted via cygpath 135 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 136 | SEP="" 137 | for dir in $ROOTDIRSRAW ; do 138 | ROOTDIRS="$ROOTDIRS$SEP$dir" 139 | SEP="|" 140 | done 141 | OURCYGPATTERN="(^($ROOTDIRS))" 142 | # Add a user-defined pattern to the cygpath arguments 143 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 144 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 145 | fi 146 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 147 | i=0 148 | for arg in "$@" ; do 149 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 150 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 151 | 152 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 153 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 154 | else 155 | eval `echo args$i`="\"$arg\"" 156 | fi 157 | i=$((i+1)) 158 | done 159 | case $i in 160 | (0) set -- ;; 161 | (1) set -- "$args0" ;; 162 | (2) set -- "$args0" "$args1" ;; 163 | (3) set -- "$args0" "$args1" "$args2" ;; 164 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 165 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 166 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 167 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 168 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 169 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 170 | esac 171 | fi 172 | 173 | # Escape application args 174 | save () { 175 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 176 | echo " " 177 | } 178 | APP_ARGS=$(save "$@") 179 | 180 | # Collect all arguments for the java command, following the shell quoting and substitution rules 181 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 182 | 183 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 184 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 185 | cd "$(dirname "$0")" 186 | fi 187 | 188 | exec "$JAVACMD" "$@" 189 | --------------------------------------------------------------------------------