├── 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 |
--------------------------------------------------------------------------------