diff --git a/README.md b/README.md index 8905f5b..642eda6 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,7 @@ Supported databases: Oracle, PostgreSQL - [Quick start](#quick-start) - [How it works](#how-it-works) +- [Build](#build) - [Security](#security) - [Bugs and feature requests](#bugs-and-feature-requests) - [Downloads](#downloads) @@ -24,7 +25,7 @@ Supported databases: Oracle, PostgreSQL - Open connection dialog and populate them with data (URL for Oracle database: **jdbc:oracle:thin:@host:port:SID**) ![ASHViewerConnectionDialog](media/connection.png) -- Press Connect button and start to monitor your system +- Press Connect button and start to monitor your system and highlight a range to show details. ![ASHViewerTop](media/top.png) - Review Raw data interface to gain a deep insight into active session history @@ -51,11 +52,31 @@ ASH Viewer provides graphical Top Activity, similar Top Activity analysis and Dr For Oracle standard edition and PostgreSQL, ASH Viewer emulate ASH, storing active session data on local storage. ** Please note that v$active_session_history is a part of the Oracle Diagnostic Pack and requires a purchase of the ODP license.** - -## Security -Passwords are stored in configuration file in encrypted form with secret key (computer name or hostname). So, when you run copied configuration on another host, you need to change password with a new secret key. -This is a minimal foolproof and for maximum protection it is necessary to store sensitive data using filesystem-level encryption or another way. - + +## Build + +```shell +mvn clean package -DskipTests=true +``` + +## Security +Encryption and Container settings provide security for database passwords (go to Other tab -> Security block) + +### Encryption +Encryption setting has AES and PBE options +- **AES** - Advanced Encryption Standard (AES) with 256-bit key encryption, from the [Bouncy Castle provider](https://www.bouncycastle.org/), [FIPS](https://www.nist.gov/standardsgov/compliance-faqs-federal-information-processing-standards-fips#:~:text=are%20FIPS%20developed%3F-,What%20are%20Federal%20Information%20Processing%20Standards%20(FIPS)%3F,by%20the%20Secretary%20of%20Commerce.) compliant algorithm; +- **PBE** - Password based encryption (PBE) in PBEWithMD5AndDES mode with secret key (computer name or hostname). This option is weak and deprecated, please use AES when possible; + +### Container +It's the way to store your encrypted data +- **DBAPI** - You sensitive data stored in Windows Data Protection API (DPAPI), maximum protected, Windows only; +- **Registry** - OS System registry storage using java preferences API - weak, but better than **Configuration**; +- **Configuration** - All data in local configuration file - weak, not recommended; + +### Recommendations +- use AES encryption and Windows Data Protection API (DPAPI) whenever possible; +- do not use PBE copied configuration on another host, you need to change password with a new secret key (always do it for password leak prevention). + ## Bugs and feature requests Have a bug or a feature request? [Please open an issue](https://github.com/akardapolov/ASH-Viewer/issues) @@ -70,6 +91,8 @@ Have a bug or a feature request? [Please open an issue](https://github.com/akard - [Berkeley DB Java Edition](http://www.oracle.com/database/berkeley-db) - [SwingLabs GUI toolkit by alexfromsun, kleopatra, rbair and other](https://en.wikipedia.org/wiki/SwingLabs) - [Dagger 2 by Google](https://dagger.dev/) +- [AES cipher by Bouncy Castle](https://www.bouncycastle.org/) +- [Windows DPAPI Wrapper by @peter-gergely-horvath](https://github.com/peter-gergely-horvath/windpapi4j) ## License [![GPLv3 license](https://img.shields.io/badge/License-GPLv3-blue.svg)](http://perso.crans.org/besson/LICENSE.html) diff --git a/ashv/pom.xml b/ashv/pom.xml index 4aa5977..027ef76 100644 --- a/ashv/pom.xml +++ b/ashv/pom.xml @@ -68,8 +68,8 @@ org.apache.maven.plugins maven-compiler-plugin - 1.8 - 1.8 + 11 + 11 diff --git a/ashv/src/main/java/config/profile/ConfigProfile.java b/ashv/src/main/java/config/profile/ConfigProfile.java index 72333c9..f2e09b4 100644 --- a/ashv/src/main/java/config/profile/ConfigProfile.java +++ b/ashv/src/main/java/config/profile/ConfigProfile.java @@ -1,6 +1,8 @@ package config.profile; import core.manager.ConstantManager; +import gui.model.ContainerType; +import gui.model.EncryptionType; import lombok.Data; import java.util.List; @@ -15,6 +17,11 @@ public class ConfigProfile { private int rawRetainDays = ConstantManager.RETAIN_DAYS_MAX; private int olapRetainDays = ConstantManager.RETAIN_DAYS_MAX; + private EncryptionType encryptionType; + private ContainerType containerType; + private String key; + private String iv; + private ConnProfile connProfile; private List sqlColProfileList; } diff --git a/ashv/src/main/java/config/security/BCFipsConfig.java b/ashv/src/main/java/config/security/BCFipsConfig.java new file mode 100644 index 0000000..de97273 --- /dev/null +++ b/ashv/src/main/java/config/security/BCFipsConfig.java @@ -0,0 +1,81 @@ +package config.security; + +import java.security.GeneralSecurityException; +import java.security.Security; +import javax.crypto.Cipher; +import javax.crypto.KeyGenerator; +import javax.crypto.SecretKey; +import javax.crypto.spec.IvParameterSpec; +import javax.inject.Inject; +import javax.inject.Singleton; +import lombok.extern.slf4j.Slf4j; +import org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider; + +/** + * This file contains API for key generation, encrypting and + * decrypting in CFB (Cipher Feedback) mode. It is similar to CBC while + * using a streaming block mode. However, padding is no longer required + * as the cipher generates a stream of "noise" which is XOR'd with the data + * to be encrypted. + * + * https://github.com/indrabasak/bouncycastle-fips-examples + * + * @author Indra Basak + * @since 11/18/2017 + */ + +@Slf4j +@Singleton +public class BCFipsConfig { + + @Inject + public BCFipsConfig() {} + + static { + Security.addProvider(new BouncyCastleFipsProvider()); + } + + /** + * Generates an AES key by providing a key size in bits. + * + * @return a symmetric key + * @throws GeneralSecurityException + */ + public SecretKey generateKey() throws GeneralSecurityException { + KeyGenerator keyGenerator = KeyGenerator.getInstance("AES", "BCFIPS"); + keyGenerator.init(256); + return keyGenerator.generateKey(); + } + + /** + * Encrypts data in CFB (Cipher Feedback) mode. + * + * @param secretKey the secret key used for encryption + * @param data the plaintext to be encrypted + * @return array with initialization vector(IV) and encrypted data + * @throws GeneralSecurityException* + */ + public byte[][] cfbEncrypt(SecretKey secretKey, byte[] data) + throws GeneralSecurityException { + Cipher cipher = Cipher.getInstance("AES/CFB/NoPadding", "BCFIPS"); + cipher.init(Cipher.ENCRYPT_MODE, secretKey); + return new byte[][]{cipher.getIV(), cipher.doFinal(data)}; + } + + /** + * Decrypts the data in CFB (Cipher Feedback) mode. + * + * @param secretKey the secret key used for decryption + * @param iv initialization vector (IV) + * @param cipherText an encrypted ciphertext + * @return array with decrypted data + * @throws GeneralSecurityException + */ + public byte[] cfbDecrypt(SecretKey secretKey, byte[] iv, + byte[] cipherText) throws GeneralSecurityException { + Cipher cipher = Cipher.getInstance("AES/CFB/NoPadding", "BCFIPS"); + cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(iv)); + return cipher.doFinal(cipherText); + } + +} diff --git a/ashv/src/main/java/config/security/ContainerConfig.java b/ashv/src/main/java/config/security/ContainerConfig.java new file mode 100644 index 0000000..bcc7fd2 --- /dev/null +++ b/ashv/src/main/java/config/security/ContainerConfig.java @@ -0,0 +1,52 @@ +package config.security; + +import gui.model.RegistryKey; +import java.util.Base64; +import java.util.prefs.Preferences; +import javax.crypto.SecretKey; +import javax.crypto.spec.SecretKeySpec; +import javax.inject.Inject; +import javax.inject.Singleton; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@Singleton +public class ContainerConfig { + private final Preferences projectPreferences; + @Inject + public ContainerConfig() { + Preferences prefsRoot = Preferences.userRoot(); + projectPreferences = prefsRoot.node("ASH-Viewer"); + } + + public String getRegistryValue(String profileName, RegistryKey registryKey) { + return projectPreferences.get(getKey(profileName, registryKey), ""); + } + + public void setRegistryValue(String profileName, RegistryKey registryKey, String value) { + projectPreferences.put(getKey(profileName, registryKey), value); + } + + private String getKey(String profileName, RegistryKey registryKey) { + return profileName + "_" + registryKey; + } + + public String convertSecretKeyToString(SecretKey secretKey) { + byte[] rawData = secretKey.getEncoded(); + return Base64.getEncoder().encodeToString(rawData); + } + + public SecretKey convertStringToSecretKey(String encodedKey) { + byte[] decodedKey = Base64.getDecoder().decode(encodedKey); + return new SecretKeySpec(decodedKey, 0, decodedKey.length, "AES"); + } + + public String convertByteToString(byte[] rawData) { + return Base64.getEncoder().encodeToString(rawData); + } + + public byte[] convertStringToByte(String encodedKey) { + return Base64.getDecoder().decode(encodedKey); + } + +} diff --git a/ashv/src/main/java/core/manager/ConfigurationManager.java b/ashv/src/main/java/core/manager/ConfigurationManager.java index 977b333..5d8ac25 100644 --- a/ashv/src/main/java/core/manager/ConfigurationManager.java +++ b/ashv/src/main/java/core/manager/ConfigurationManager.java @@ -1,28 +1,51 @@ package core.manager; +import com.github.windpapi4j.InitializationFailedException; +import com.github.windpapi4j.WinAPICallFailedException; +import com.github.windpapi4j.WinDPAPI; +import com.github.windpapi4j.WinDPAPI.CryptProtectFlag; import config.profile.ConfigProfile; import config.profile.ConnProfile; import config.profile.SqlColProfile; +import config.security.BCFipsConfig; +import config.security.ContainerConfig; import config.security.PassConfig; import config.yaml.YamlConfig; import core.manager.ConstantManager.Profile; import core.parameter.ConnectionBuilder; import excp.SqlColMetadataException; -import lombok.Getter; -import lombok.Setter; -import lombok.extern.slf4j.Slf4j; -import profile.*; - +import gui.model.ContainerType; +import gui.model.EncryptionType; +import gui.model.RegistryKey; +import java.security.GeneralSecurityException; +import java.util.AbstractMap; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.TreeMap; +import javax.crypto.SecretKey; import javax.inject.Inject; import javax.inject.Named; import javax.inject.Singleton; -import java.util.*; +import lombok.Getter; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import profile.IProfile; +import profile.OracleEE; +import profile.OracleEE10g; +import profile.OracleEEObject; +import profile.OracleSE; +import profile.Postgres; +import profile.Postgres96; @Slf4j @Singleton public class ConfigurationManager { private YamlConfig yamlConfig; private PassConfig passConfig; + private BCFipsConfig bcFipsConfig; + private ContainerConfig containerConfig; private TreeMap configList; @Getter @Setter private ConfigProfile currentConfiguration; @@ -32,9 +55,13 @@ public class ConfigurationManager { @Inject public ConfigurationManager(YamlConfig yamlConfig, PassConfig passConfig, + BCFipsConfig bcFipsConfig, + ContainerConfig containerConfig, @Named("ConfigList") TreeMap configList) { this.yamlConfig = yamlConfig; this.passConfig = passConfig; + this.bcFipsConfig = bcFipsConfig; + this.containerConfig = containerConfig; this.configList = configList; } @@ -81,43 +108,52 @@ public void deleteConfig(String configurationName) { } public ConnectionBuilder getConnectionParameters(String connName) { - Map.Entry orElseEntry = - new AbstractMap.SimpleImmutableEntry<>(connName, new ConfigProfile()); - orElseEntry.getValue().setConfigName(connName); - orElseEntry.getValue().setConnProfile(new ConnProfile()); - orElseEntry.getValue().getConnProfile().setPassword(""); + Map.Entry other = new AbstractMap.SimpleImmutableEntry<>(connName, new ConfigProfile()); + other.getValue().setConfigName(connName); + other.getValue().setConnProfile(new ConnProfile()); + other.getValue().getConnProfile().setPassword(""); Map.Entry cfg = configList.entrySet().stream() .filter(e -> e.getValue().getConfigName().equalsIgnoreCase(connName)) - .findAny().orElse(orElseEntry); + .findAny().orElse(other); ConnProfile connOut = cfg.getValue().getConnProfile(); return new ConnectionBuilder.Builder(connName) .userName(connOut.getUserName()) - .password(passConfig.decrypt(connOut.getPassword())) + .password(getPassword(cfg.getValue())) .url(connOut.getUrl()) .jar(connOut.getJar()) .profile(connOut.getProfileName()) .initialLoading(String.valueOf(cfg.getValue().getInitialLoading())) .rawRetainDays(String.valueOf(cfg.getValue().getRawRetainDays())) .olapRetainDays(String.valueOf(cfg.getValue().getOlapRetainDays())) + .containerType(cfg.getValue().getContainerType()) + .encryptionType(cfg.getValue().getEncryptionType()) .build(); } public void saveConnection(ConnectionBuilder connIn) { - Map.Entry orElseEntry = new AbstractMap.SimpleImmutableEntry<>("", new ConfigProfile()); - orElseEntry.getValue().setConnProfile(new ConnProfile()); - orElseEntry.getValue().setConfigName(connIn.getConnectionName()); + Map.Entry other = new AbstractMap.SimpleImmutableEntry<>("", new ConfigProfile()); + other.getValue().setConnProfile(new ConnProfile()); + other.getValue().setConfigName(connIn.getConnectionName()); Map.Entry cfg = configList.entrySet().stream() .filter(e -> e.getValue().getConfigName().equalsIgnoreCase(connIn.getConnectionName())) - .findAny().orElse(orElseEntry); + .findAny().orElse(other); + + try { + savePassword(connIn, cfg); + } catch (GeneralSecurityException e) { + log.error(e.getMessage()); + throw new RuntimeException(e); + } + connIn.cleanPassword(); + ConnProfile connOut = cfg.getValue().getConnProfile(); connOut.setConnName(connIn.getConnectionName()); connOut.setUserName(connIn.getUserName()); - connOut.setPassword(passConfig.encrypt(connIn.getPassword())); connOut.setUrl(connIn.getUrl()); connOut.setJar(connIn.getJar()); connOut.setProfileName(connIn.getProfile()); @@ -126,10 +162,180 @@ public void saveConnection(ConnectionBuilder connIn) { cfg.getValue().setInitialLoading(Integer.parseInt(connIn.getInitialLoading())); cfg.getValue().setRawRetainDays(getRawRetainDays()); cfg.getValue().setOlapRetainDays(getOlapRetainDays()); + cfg.getValue().setEncryptionType(connIn.getEncryptionType()); + cfg.getValue().setContainerType(connIn.getContainerType()); yamlConfig.saveConfigToFile(cfg.getValue()); } + private void savePassword(ConnectionBuilder connIn, Map.Entry cfg) throws GeneralSecurityException { + EncryptionType encryptionTypeCurrent = connIn.getEncryptionType(); + ContainerType containerTypeCurrent = connIn.getContainerType(); + + ConnProfile connOut = cfg.getValue().getConnProfile(); + + // Clean values from previous configuration + ContainerType containerTypePrev = cfg.getValue().getContainerType(); + EncryptionType encryptionTypePrev = cfg.getValue().getEncryptionType(); + if (containerTypeCurrent != null && encryptionTypeCurrent != null + && ContainerType.CONFIGURATION.equals(containerTypePrev) + && containerTypeCurrent.equals(containerTypePrev) + && !encryptionTypeCurrent.equals(encryptionTypePrev)) { + cfg.getValue().setKey(null); + cfg.getValue().setIv(null); + connOut.setPassword(null); + } + + // Save current values + if (EncryptionType.PBE.equals(encryptionTypeCurrent)) { + String passwordEncrypted = passConfig.encrypt(connIn.getPassword()); + if (ContainerType.DPAPI.equals(containerTypeCurrent)) { + if(WinDPAPI.isPlatformSupported()) { + try { + WinDPAPI winDPAPI = WinDPAPI.newInstance(CryptProtectFlag.CRYPTPROTECT_UI_FORBIDDEN); + byte[] cipherTextBytes = winDPAPI.protectData(passwordEncrypted.getBytes()); + cfg.getValue().setKey(containerConfig.convertByteToString(cipherTextBytes)); + + cfg.getValue().setIv(null); + connOut.setPassword(null); + } catch (InitializationFailedException | WinAPICallFailedException e) { + throw new RuntimeException(e); + } + } else { + throw new RuntimeException("Windows Data Protection API (DPAPI) as secure container is not supported"); + } + } else if (ContainerType.REGISTRY.equals(containerTypeCurrent)) { + containerConfig.setRegistryValue(cfg.getValue().getConfigName(), RegistryKey.PASSWORD, passwordEncrypted); + + cfg.getValue().setKey(null); + cfg.getValue().setIv(null); + connOut.setPassword(null); + } else if (ContainerType.CONFIGURATION.equals(containerTypeCurrent)) { + connOut.setPassword(passwordEncrypted); + + cfg.getValue().setKey(null); + cfg.getValue().setIv(null); + } + } else if (EncryptionType.AES.equals(encryptionTypeCurrent)) { + SecretKey secretKey = bcFipsConfig.generateKey(); + byte[][] passwordEncrypted = bcFipsConfig.cfbEncrypt(secretKey, connIn.getPassword().getBytes()); + + if (ContainerType.DPAPI.equals(containerTypeCurrent)) { + if(WinDPAPI.isPlatformSupported()) { + try { + WinDPAPI winDPAPI = WinDPAPI.newInstance(CryptProtectFlag.CRYPTPROTECT_UI_FORBIDDEN); + byte[] cipherKeyBytes = winDPAPI.protectData(containerConfig.convertSecretKeyToString(secretKey).getBytes()); + byte[] cipherIvBytes = winDPAPI.protectData(containerConfig.convertByteToString(passwordEncrypted[0]).getBytes()); + byte[] cipherPassEncBytes = winDPAPI.protectData(containerConfig.convertByteToString(passwordEncrypted[1]).getBytes()); + + cfg.getValue().setKey(containerConfig.convertByteToString(cipherKeyBytes)); + cfg.getValue().setIv(containerConfig.convertByteToString(cipherIvBytes)); + connOut.setPassword(containerConfig.convertByteToString(cipherPassEncBytes)); + } catch (InitializationFailedException | WinAPICallFailedException e) { + throw new RuntimeException(e); + } + } else { + throw new RuntimeException("Windows Data Protection API (DPAPI) as secure container is not supported"); + } + } else if (ContainerType.REGISTRY.equals(containerTypeCurrent)) { + String cipherKey = containerConfig.convertSecretKeyToString(secretKey); + String cipherIv = containerConfig.convertByteToString(passwordEncrypted[0]); + String cipherPassEnc = containerConfig.convertByteToString(passwordEncrypted[1]); + + containerConfig.setRegistryValue(cfg.getValue().getConfigName(), RegistryKey.KEY, cipherKey); + containerConfig.setRegistryValue(cfg.getValue().getConfigName(), RegistryKey.IV, cipherIv); + containerConfig.setRegistryValue(cfg.getValue().getConfigName(), RegistryKey.PASSWORD, cipherPassEnc); + + cfg.getValue().setKey(null); + cfg.getValue().setIv(null); + connOut.setPassword(null); + } else if (ContainerType.CONFIGURATION.equals(containerTypeCurrent)) { + cfg.getValue().setKey(containerConfig.convertSecretKeyToString(secretKey)); + cfg.getValue().setIv(containerConfig.convertByteToString(passwordEncrypted[0])); + connOut.setPassword(containerConfig.convertByteToString(passwordEncrypted[1])); + } + } + } + + public String getPassword(ConfigProfile configProfile) { + EncryptionType encryptionType = configProfile.getEncryptionType(); + ContainerType containerType = configProfile.getContainerType(); + + if (encryptionType == null && containerType == null) { + return passConfig.decrypt(configProfile.getConnProfile().getPassword()); + } + + String password = ""; + + if (EncryptionType.PBE.equals(encryptionType)) { + if (ContainerType.DPAPI.equals(containerType)) { + if(WinDPAPI.isPlatformSupported()) { + try { + WinDPAPI winDPAPI = WinDPAPI.newInstance(CryptProtectFlag.CRYPTPROTECT_UI_FORBIDDEN); + byte[] decryptedBytes = winDPAPI.unprotectData(containerConfig.convertStringToByte(configProfile.getKey())); + password = passConfig.decrypt(new String(decryptedBytes)); + } catch (InitializationFailedException | WinAPICallFailedException e) { + throw new RuntimeException(e); + } + } else { + throw new RuntimeException("Windows Data Protection API (DPAPI) as secure container is not supported"); + } + } else if (ContainerType.REGISTRY.equals(containerType)) { + password = passConfig.decrypt(containerConfig.getRegistryValue(configProfile.getConfigName(), RegistryKey.PASSWORD)); + } else if (ContainerType.CONFIGURATION.equals(containerType)) { + password = passConfig.decrypt(configProfile.getConnProfile().getPassword()); + } + } else if (EncryptionType.AES.equals(encryptionType)) { + if (ContainerType.DPAPI.equals(containerType)) { + if(WinDPAPI.isPlatformSupported()) { + try { + WinDPAPI winDPAPI = WinDPAPI.newInstance(CryptProtectFlag.CRYPTPROTECT_UI_FORBIDDEN); + + String secretKeyStr = new String(winDPAPI.unprotectData(containerConfig.convertStringToByte(configProfile.getKey()))); + String ivStr = new String(winDPAPI.unprotectData(containerConfig.convertStringToByte(configProfile.getIv()))); + String pwdStr = new String(winDPAPI.unprotectData(containerConfig.convertStringToByte(configProfile.getConnProfile().getPassword()))); + + try { + password = new String(bcFipsConfig.cfbDecrypt(containerConfig.convertStringToSecretKey(secretKeyStr), + containerConfig.convertStringToByte(ivStr), containerConfig.convertStringToByte(pwdStr))); + } catch (GeneralSecurityException e) { + throw new RuntimeException(e); + } + + } catch (InitializationFailedException | WinAPICallFailedException e) { + throw new RuntimeException(e); + } + } else { + throw new RuntimeException("Windows Data Protection API (DPAPI) as secure container is not supported"); + } + } else if (ContainerType.REGISTRY.equals(containerType)) { + String secretKeyStr = containerConfig.getRegistryValue(configProfile.getConfigName(), RegistryKey.KEY); + String ivStr = containerConfig.getRegistryValue(configProfile.getConfigName(), RegistryKey.IV); + String pwdStr = containerConfig.getRegistryValue(configProfile.getConfigName(), RegistryKey.PASSWORD); + + try { + password = new String(bcFipsConfig.cfbDecrypt(containerConfig.convertStringToSecretKey(secretKeyStr), + containerConfig.convertStringToByte(ivStr), containerConfig.convertStringToByte(pwdStr))); + } catch (GeneralSecurityException e) { + throw new RuntimeException(e); + } + } else if (ContainerType.CONFIGURATION.equals(containerType)) { + SecretKey secretKey = containerConfig.convertStringToSecretKey(configProfile.getKey()); + byte[] iv = containerConfig.convertStringToByte(configProfile.getIv()); + byte[] passwordEncrypted = containerConfig.convertStringToByte(configProfile.getConnProfile().getPassword()); + + try { + password = new String(bcFipsConfig.cfbDecrypt(secretKey, iv, passwordEncrypted)); + } catch (GeneralSecurityException e) { + throw new RuntimeException(e); + } + } + + } + + return password; + } + public int getRawRetainDays() { return ConstantManager.RETAIN_DAYS_MAX; } @@ -168,31 +374,4 @@ public IProfile getProfileImpl(String profileName) { } } - /** - * For migration purposes (encrypt passwords in yaml config) - * Delete in future release - */ - @Deprecated - public void updatePassword() { - configList.entrySet().stream().forEach(e -> { - String pass = e.getValue().getConnProfile().getPassword(); - try { - passConfig.decrypt(pass); - } catch (Exception e1){ - log.error(e1.getLocalizedMessage()); - e.getValue().getConnProfile().setPassword(passConfig.encrypt(pass)); - } - }); - - TreeMap shCopy = new TreeMap<>(configList); - - synchronized (shCopy) { - Iterator> - i = shCopy.entrySet().iterator(); - while (i.hasNext()) { - loadConfigToFile(i.next().getValue()); - } - } - } - } diff --git a/ashv/src/main/java/core/parameter/ConnectionBuilder.java b/ashv/src/main/java/core/parameter/ConnectionBuilder.java index 2b4ad66..7b6d424 100644 --- a/ashv/src/main/java/core/parameter/ConnectionBuilder.java +++ b/ashv/src/main/java/core/parameter/ConnectionBuilder.java @@ -1,9 +1,12 @@ package core.parameter; +import gui.model.ContainerType; +import gui.model.EncryptionType; + public class ConnectionBuilder { private final String connectionName; private final String userName; - private final String password; + private String password; private final String url; private final String jar; private final String profile; @@ -11,6 +14,8 @@ public class ConnectionBuilder { private final String initialLoading; private final String rawRetainDays; private final String olapRetainDays; + private final EncryptionType encryptionType; + private final ContainerType containerType; public String getConnectionName() { return connectionName; } public String getUserName() { return userName; } @@ -22,6 +27,12 @@ public class ConnectionBuilder { public String getInitialLoading() { return initialLoading; } public String getRawRetainDays() { return rawRetainDays; } public String getOlapRetainDays() { return olapRetainDays; } + public EncryptionType getEncryptionType() { return encryptionType; } + public ContainerType getContainerType() { return containerType; } + + public void cleanPassword() { + password = ""; + } public static class Builder { private final String connectionName; @@ -34,6 +45,8 @@ public static class Builder { private String initialLoading; private String rawRetainDays; private String olapRetainDays; + private EncryptionType encryptionType; + private ContainerType containerType; public Builder(String connectionName) { this.connectionName = connectionName; @@ -48,6 +61,8 @@ public Builder(String connectionName) { public Builder initialLoading(String il) { initialLoading = il; return this; } public Builder rawRetainDays(String raw) { rawRetainDays = raw; return this; } public Builder olapRetainDays(String raw) { olapRetainDays = raw; return this; } + public Builder encryptionType(EncryptionType raw) { encryptionType = raw; return this; } + public Builder containerType(ContainerType raw) { containerType = raw; return this; } public ConnectionBuilder build() { return new ConnectionBuilder(this); @@ -65,6 +80,8 @@ private ConnectionBuilder(Builder builder){ initialLoading = builder.initialLoading; rawRetainDays = builder.rawRetainDays; olapRetainDays = builder.olapRetainDays; + encryptionType = builder.encryptionType; + containerType = builder.containerType; } } diff --git a/ashv/src/main/java/core/processing/GetFromRemoteAndStore.java b/ashv/src/main/java/core/processing/GetFromRemoteAndStore.java index 520c276..d8d6a12 100644 --- a/ashv/src/main/java/core/processing/GetFromRemoteAndStore.java +++ b/ashv/src/main/java/core/processing/GetFromRemoteAndStore.java @@ -2,6 +2,7 @@ import com.sleepycat.persist.EntityCursor; import config.Labels; +import config.profile.ConfigProfile; import config.profile.ConnProfile; import config.profile.SqlColProfile; import core.manager.ColorManager; @@ -20,7 +21,6 @@ import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; -import java.sql.Statement; import java.sql.Timestamp; import java.time.Instant; import java.time.LocalDateTime; @@ -127,11 +127,11 @@ public GetFromRemoteAndStore(ColorManager colorManager, this.olapDAO = olapDAO; } - public void initConnection(ConnProfile connProfile) throws SQLException{ + public void initConnection(ConfigProfile configProfile) throws SQLException{ this.chartDatasetManager.setGetFromRemoteAndStore(this); - this.connProfile = connProfile; - this.remoteDBManager.init(connProfile); + this.connProfile = configProfile.getConnProfile(); + this.remoteDBManager.init(configProfile); this.initializeConnection(); } @@ -584,13 +584,13 @@ private void initializeConnection() throws SQLException { } private List loadSqlMetaData(String sqlName, String sqlText) { - Statement s = null; + PreparedStatement s = null; ResultSet rs = null; List sqlColProfileList = new ArrayList<>(); try { - s = connection.createStatement(); - s.executeQuery(sqlText); + s = connection.prepareStatement(sqlText); + s.executeQuery(); rs = s.getResultSet(); ResultSetMetaData rsmd = rs.getMetaData(); for (int i = 1; i <= rsmd.getColumnCount(); i++) { diff --git a/ashv/src/main/java/gui/connect/ConnectToDbArea.java b/ashv/src/main/java/gui/connect/ConnectToDbArea.java index 089df0c..2cb83e7 100644 --- a/ashv/src/main/java/gui/connect/ConnectToDbArea.java +++ b/ashv/src/main/java/gui/connect/ConnectToDbArea.java @@ -1,7 +1,9 @@ package gui.connect; +import com.github.windpapi4j.WinDPAPI; import config.GUIConfig; import config.Labels; +import config.profile.ConfigProfile; import config.profile.ConnProfile; import core.manager.ColorManager; import core.manager.ConfigurationManager; @@ -13,6 +15,8 @@ import gui.MonitorDbPanel; import gui.chart.ChartDatasetManager; import gui.custom.HintTextField; +import gui.model.ContainerType; +import gui.model.EncryptionType; import gui.util.ProgressBarUtil; import java.awt.Color; import java.awt.Component; @@ -115,7 +119,6 @@ public class ConnectToDbArea extends JDialog { private JLabel profileMessageLbl = new JLabel(Labels.getLabel("gui.connection.profile.message")); private JLabel offlineLbl = new JLabel(Labels.getLabel("gui.connection.offline")); - private JPanel initialLoadPanel; private JLabel separatorInitialLoadingLbl = new JLabel(Labels.getLabel("gui.connection.initial.loading")); private JLabel initialLoadingOracleEELbl = new JLabel(Labels.getLabel("gui.connection.initial.loading.oracle")); @@ -128,9 +131,21 @@ public class ConnectToDbArea extends JDialog { private JRadioButton initialLoadAllRButton; private JRadioButton initialLoadLastRButton; + private JPanel securityCipherPanel; + private JPanel securityContainerPanel; + private JRadioButton securityPBECipherRButton; + private JRadioButton securityBCFipsAesCipherRButton; + + private JRadioButton securityContainerConfiguration; + private JRadioButton securityContainerRegistry; + private JRadioButton securityContainerWindowsDPAPI; + private JLabel separatorRetainLbl = new JLabel(Labels.getLabel("gui.connection.retain")); private JLabel retainRawDataLbl = new JLabel(Labels.getLabel("gui.connection.retain.raw")); private JLabel retainOlapDataLbl = new JLabel(Labels.getLabel("gui.connection.retain.olap")); + private JLabel securityLbl = new JLabel(Labels.getLabel("gui.connection.security")); + private JLabel securityCipherLbl = new JLabel(Labels.getLabel("gui.connection.security.cipher")); + private JLabel securityContainerLbl = new JLabel(Labels.getLabel("gui.connection.security.container")); private JTextField connNameTF = new JTextField(); private JTextField usernameTF = new JTextField(); @@ -185,10 +200,6 @@ private void init(){ configOtherJPanel = new JPanel(lmConnOther); buttonPanel = new JPanel(lmButtonPanel); - //////////////////////// Delete it in future release /////////////////// - configurationManager.updatePassword(); - //////////////////////// Delete it in future release /////////////////// - this.init_gui(); this.add(mainJPanel); @@ -323,6 +334,83 @@ private void init_gui(){ } }); + MigLayout securityMigLayout = new MigLayout("", "[30lp][30lp][30lp][30lp]"); + MigLayout securityContainerMigLayout = new MigLayout("", "[30lp][30lp][30lp][30lp][30lp][30lp]"); + securityCipherPanel = new JPanel(securityMigLayout); + securityContainerPanel = new JPanel(securityContainerMigLayout); + + securityBCFipsAesCipherRButton = new JRadioButton(Labels.getLabel("gui.connection.security.cipher.aes")); + securityPBECipherRButton = new JRadioButton(Labels.getLabel("gui.connection.security.cipher.pbe")); + securityBCFipsAesCipherRButton.setToolTipText(Labels.getLabel("gui.connection.security.cipher.aes.tooltip")); + securityPBECipherRButton.setToolTipText(Labels.getLabel("gui.connection.security.cipher.pbe.tooltip")); + + securityContainerWindowsDPAPI = new JRadioButton(Labels.getLabel("gui.connection.security.container.dbapi")); + securityContainerRegistry = new JRadioButton(Labels.getLabel("gui.connection.security.container.registry")); + securityContainerConfiguration = new JRadioButton(Labels.getLabel("gui.connection.security.container.configuration")); + securityContainerWindowsDPAPI.setToolTipText(Labels.getLabel("gui.connection.security.container.dbapi.tooltip")); + securityContainerRegistry.setToolTipText(Labels.getLabel("gui.connection.security.container.registry.tooltip")); + securityContainerConfiguration.setToolTipText(Labels.getLabel("gui.connection.security.container.configuration.tooltip")); + + securityBCFipsAesCipherRButton.addChangeListener(event -> { + AbstractButton aButton = (AbstractButton) event.getSource(); + ButtonModel model = aButton.getModel(); + if (model.isSelected()) { + securityBCFipsAesCipherRButton.setSelected(true); + securityPBECipherRButton.setSelected(false); + } + }); + + securityPBECipherRButton.addChangeListener(event -> { + AbstractButton aButton = (AbstractButton) event.getSource(); + ButtonModel model = aButton.getModel(); + if (model.isSelected()) { + securityPBECipherRButton.setSelected(true); + securityBCFipsAesCipherRButton.setSelected(false); + } + }); + + securityContainerWindowsDPAPI.addItemListener(evt -> { + if(evt.getStateChange() == ItemEvent.SELECTED){ + securityContainerWindowsDPAPI.setSelected(true); + securityContainerRegistry.setSelected(false); + securityContainerConfiguration.setSelected(false); + + if (!WinDPAPI.isPlatformSupported()) { + String message = "Please, select another option - Registry or Configuration"; + log.info(message); + JOptionPane.showConfirmDialog(this, + "Windows Data Protection API (DPAPI) as secure container is not supported. " + message, + "Information", JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE); + } + } + }); + + securityContainerRegistry.addItemListener(evt -> { + if(evt.getStateChange() == ItemEvent.SELECTED){ + securityContainerRegistry.setSelected(true); + securityContainerWindowsDPAPI.setSelected(false); + securityContainerConfiguration.setSelected(false); + } + }); + + securityContainerConfiguration.addItemListener(evt -> { + if(evt.getStateChange() == ItemEvent.SELECTED){ + securityContainerConfiguration.setSelected(true); + securityContainerWindowsDPAPI.setSelected(false); + securityContainerRegistry.setSelected(false); + } + }); + + securityCipherPanel.add(securityBCFipsAesCipherRButton, "gap 1"); + securityCipherPanel.add(new JSeparator(SwingConstants.VERTICAL), "growy, hmin 10, alignx center"); + securityCipherPanel.add(securityPBECipherRButton, "gap 1"); + + securityContainerPanel.add(securityContainerWindowsDPAPI, "gap 1"); + securityContainerPanel.add(new JSeparator(SwingConstants.VERTICAL), "growy, hmin 10, alignx center"); + securityContainerPanel.add(securityContainerRegistry, "gap 1"); + securityContainerPanel.add(new JSeparator(SwingConstants.VERTICAL), "growy, hmin 10, alignx center"); + securityContainerPanel.add(securityContainerConfiguration, "gap 1"); + initialLoadPanel.add(initialLoadAllRButton, "gap 1"); initialLoadPanel.add(new JSeparator(SwingConstants.VERTICAL), "growy, hmin 10, alignx center"); initialLoadPanel.add(initialLoadLastRButton, "gap 1"); @@ -344,6 +432,15 @@ private void init_gui(){ configOtherJPanel.add(olapDataDaysRetainTF, "span, growx, wmin 150"); olapDataDaysRetainTF.setToolTipText(Labels.getLabel("gui.connection.retain.olap.tooltip")); + securityLbl.setForeground(LABEL_COLOR); + configOtherJPanel.add(securityLbl, "gapbottom 1, span, split 2, aligny center"); + configOtherJPanel.add(new JSeparator(), "gapleft rel, growx"); + + configOtherJPanel.add(securityCipherLbl, "skip"); + configOtherJPanel.add(securityCipherPanel, "span, growx, wmin 100"); + configOtherJPanel.add(securityContainerLbl, "skip"); + configOtherJPanel.add(securityContainerPanel, "span, growx, wmin 100"); + jButtonConnect.addActionListener(e -> { if (isOffline.isSelected()){ ProgressBarUtil.runProgressDialog(this::loadObjectsByConnectionNameOffline, @@ -490,6 +587,13 @@ private void setDetailEditable(boolean bParameter){ initialLoadAllRButton.setEnabled(bParameter); initialLoadLastRButton.setEnabled(bParameter); initialLoadSpinner.setEnabled(bParameter); + + securityBCFipsAesCipherRButton.setEnabled(bParameter); + securityPBECipherRButton.setEnabled(bParameter); + + securityContainerWindowsDPAPI.setEnabled(bParameter); + securityContainerRegistry.setEnabled(bParameter); + securityContainerConfiguration.setEnabled(bParameter); } private void clearProfileFields(){ @@ -503,6 +607,18 @@ private void clearProfileFields(){ initialLoadAllRButton.setSelected(true); initialLoadLastRButton.setSelected(false); + + securityPBECipherRButton.setEnabled(true); + securityBCFipsAesCipherRButton.setEnabled(true); + securityPBECipherRButton.setSelected(false); + securityBCFipsAesCipherRButton.setSelected(false); + + securityContainerRegistry.setEnabled(true); + securityContainerConfiguration.setEnabled(true); + securityContainerWindowsDPAPI.setEnabled(true); + securityContainerRegistry.setSelected(false); + securityContainerConfiguration.setSelected(false); + securityContainerWindowsDPAPI.setSelected(false); } private void copyConnection(){ @@ -548,12 +664,45 @@ private void selectFromDbAndSetInGui(String connName){ if (!olapDataDaysRetainTF.isEnabled()) { setOlapDataDaysRetainTF(olapDataDaysRetainTF, Integer.parseInt(connParameters.getOlapRetainDays())); } + + if (connParameters.getEncryptionType() != null) { + if (EncryptionType.AES.equals(connParameters.getEncryptionType())) { + securityBCFipsAesCipherRButton.setSelected(true); + } + if (EncryptionType.PBE.equals(connParameters.getEncryptionType())) { + securityPBECipherRButton.setSelected(true); + } + securityBCFipsAesCipherRButton.setEnabled(false); + securityPBECipherRButton.setEnabled(false); + } else { + securityBCFipsAesCipherRButton.setSelected(false); + securityPBECipherRButton.setSelected(false); + } + + if (connParameters.getContainerType() != null) { + if (ContainerType.DPAPI.equals(connParameters.getContainerType())) { + securityContainerWindowsDPAPI.setSelected(true); + } + if (ContainerType.REGISTRY.equals(connParameters.getContainerType())) { + securityContainerRegistry.setSelected(true); + } + if (ContainerType.CONFIGURATION.equals(connParameters.getContainerType())) { + securityContainerConfiguration.setSelected(true); + } + securityContainerWindowsDPAPI.setEnabled(false); + securityContainerRegistry.setEnabled(false); + securityContainerConfiguration.setEnabled(false); + } else { + securityContainerRegistry.setSelected(false); + securityContainerConfiguration.setSelected(false); + securityContainerWindowsDPAPI.setSelected(false); + } } private void saveData(){ ConnectionBuilder connParameters = new ConnectionBuilder.Builder(connNameTF.getText()) .userName(usernameTF.getText()) - .password(passwordTF.getText()) + .password(String.valueOf(passwordTF.getPassword())) .url(urlTF.getText()) .jar(jarTF.getText()) .profile(String.valueOf((profileBox.getSelectedItem()))) @@ -562,9 +711,29 @@ private void saveData(){ .initialLoading(initialLoadAllRButton.isSelected() ? "-1" : initialLoadSpinner.getValue().toString()) .rawRetainDays(rawDataDaysRetainTF.getText()) .olapRetainDays(olapDataDaysRetainTF.getText()) + .encryptionType(getEncryptionType()) + .containerType(getContainerType()) .build(); configurationManager.saveConnection(connParameters); + + connParameters.cleanPassword(); + } + + private EncryptionType getEncryptionType() { + return securityBCFipsAesCipherRButton.isSelected() ? EncryptionType.AES : EncryptionType.PBE; + } + + private ContainerType getContainerType() { + if (securityContainerWindowsDPAPI.isSelected()) { + return ContainerType.DPAPI; + } else if (securityContainerRegistry.isSelected()) { + return ContainerType.REGISTRY; + } else if (securityContainerConfiguration.isSelected()) { + return ContainerType.CONFIGURATION; + } else { + return ContainerType.CONFIGURATION; + } } private void deleteConfig(){ @@ -589,14 +758,14 @@ private void loadObjectsByConnectionName() { try { configurationManager.loadCurrentConfiguration(connNameTF.getText()); - ConnProfile connection = configurationManager.getCurrentConfiguration().getConnProfile(); + ConfigProfile configProfile = configurationManager.getCurrentConfiguration(); - getFromRemoteAndStore.initConnection(connection); + getFromRemoteAndStore.initConnection(configProfile); getFromRemoteAndStore.initProfile(configurationManager.getIProfile()); chartDatasetManager.setIProfile(configurationManager.getIProfile()); - monitorDbPanel.setConnProfile(connection); + monitorDbPanel.setConnProfile(configProfile.getConnProfile()); monitorDbPanel.initialize(); getFromRemoteAndStore.loadDataFromRemoteToLocalStore(); diff --git a/ashv/src/main/java/gui/model/ContainerType.java b/ashv/src/main/java/gui/model/ContainerType.java new file mode 100644 index 0000000..c67f673 --- /dev/null +++ b/ashv/src/main/java/gui/model/ContainerType.java @@ -0,0 +1,7 @@ +package gui.model; + +public enum ContainerType { + DPAPI, + REGISTRY, + CONFIGURATION; +} diff --git a/ashv/src/main/java/gui/model/EncryptionType.java b/ashv/src/main/java/gui/model/EncryptionType.java new file mode 100644 index 0000000..dbc2751 --- /dev/null +++ b/ashv/src/main/java/gui/model/EncryptionType.java @@ -0,0 +1,6 @@ +package gui.model; + +public enum EncryptionType { + AES, + PBE; +} diff --git a/ashv/src/main/java/gui/model/RegistryKey.java b/ashv/src/main/java/gui/model/RegistryKey.java new file mode 100644 index 0000000..ff5c098 --- /dev/null +++ b/ashv/src/main/java/gui/model/RegistryKey.java @@ -0,0 +1,7 @@ +package gui.model; + +public enum RegistryKey { + KEY, + IV, + PASSWORD; +} diff --git a/ashv/src/main/java/remote/RemoteDBManager.java b/ashv/src/main/java/remote/RemoteDBManager.java index 50acf6e..db041ce 100644 --- a/ashv/src/main/java/remote/RemoteDBManager.java +++ b/ashv/src/main/java/remote/RemoteDBManager.java @@ -1,45 +1,43 @@ package remote; +import config.profile.ConfigProfile; +import config.profile.ConnProfile; +import core.manager.ConfigurationManager; +import core.manager.ConstantManager.Profile; import java.io.File; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; import java.sql.Connection; +import java.sql.PreparedStatement; import java.sql.SQLException; -import java.sql.Statement; - import javax.inject.Inject; import javax.inject.Singleton; - -import org.apache.commons.dbcp2.BasicDataSource; - -import config.profile.ConnProfile; -import config.security.PassConfig; -import core.manager.ConstantManager.Profile; import lombok.Getter; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.dbcp2.BasicDataSource; @Slf4j @Singleton public class RemoteDBManager { - private PassConfig passConfig; + private ConfigurationManager configurationManager; private ConnProfile connProfile; @Getter private BasicDataSource basicDataSource; @Inject - public RemoteDBManager(PassConfig passConfig){ - this.passConfig = passConfig; + public RemoteDBManager(ConfigurationManager configurationManager) { + this.configurationManager = configurationManager; } - public void init(ConnProfile connProfile) { + public void init(ConfigProfile configProfile) { try { - this.connProfile = connProfile; + this.connProfile = configProfile.getConnProfile(); this.basicDataSource = new BasicDataSource(); this.basicDataSource.setDriverClassLoader(getClassLoader()); this.basicDataSource.setDriverClassName(this.connProfile.getDriver()); this.basicDataSource.setUrl(this.connProfile.getUrl()); this.basicDataSource.setUsername(this.connProfile.getUserName()); - this.basicDataSource.setPassword(passConfig.decrypt(this.connProfile.getPassword())); + this.basicDataSource.setPassword(configurationManager.getPassword(configProfile)); this.basicDataSource.setInitialSize(3); this.basicDataSource.setMaxTotal(5); @@ -62,8 +60,8 @@ private void bootstrapConnection(Connection connection) throws SQLException { case OracleEEObject : case OracleSE : log.debug("Setting optimizer_mode = 'ALL_ROWS'"); - try (Statement stmt = connection.createStatement() ) { - stmt.executeUpdate("ALTER SESSION SET optimizer_mode = 'ALL_ROWS'"); + try (PreparedStatement stmt = connection.prepareStatement("ALTER SESSION SET optimizer_mode = 'ALL_ROWS'")) { + stmt.executeUpdate(); } break; case Postgres : diff --git a/ashv/src/main/resources/messages.properties b/ashv/src/main/resources/messages.properties index e6cf272..f4ea9ac 100644 --- a/ashv/src/main/resources/messages.properties +++ b/ashv/src/main/resources/messages.properties @@ -46,6 +46,21 @@ gui.connection.retain.olap=Olap gui.connection.retain.raw.tooltip=The feature is under development gui.connection.retain.olap.tooltip=The feature is under development +gui.connection.security=Security +gui.connection.security.cipher=Cipher +gui.connection.security.cipher.aes=AES +gui.connection.security.cipher.pbe=PBE +gui.connection.security.cipher.aes.tooltip=AES type encryption (Bouncy Castle FIPS complaint, recommended) +gui.connection.security.cipher.pbe.tooltip=PBEWithMD5AndDES type encryption (weak and deprecated, please use AES) + +gui.connection.security.container=Container +gui.connection.security.container.dbapi=DBAPI +gui.connection.security.container.registry=Registry +gui.connection.security.container.configuration=Configuration +gui.connection.security.container.dbapi.tooltip=Windows Data Protection API (DPAPI) - maximum protected, recommended, Windows only +gui.connection.security.container.registry.tooltip=System registry using java preferences API - weak, but better than Configuration +gui.connection.security.container.configuration.tooltip=Local configuration file - weak, not recommended + gui.connection.button.connect=Connect gui.connection.button.connect.running=Running.. gui.connection.button.new=New diff --git a/ashv/src/test/java/config/security/BCFipsConfigTest.java b/ashv/src/test/java/config/security/BCFipsConfigTest.java new file mode 100644 index 0000000..8ea12c2 --- /dev/null +++ b/ashv/src/test/java/config/security/BCFipsConfigTest.java @@ -0,0 +1,37 @@ +package config.security; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertNotNull; + +import java.security.GeneralSecurityException; +import javax.crypto.SecretKey; +import org.junit.Before; +import org.junit.Test; + +public class BCFipsConfigTest { + private BCFipsConfig bcFipsConfig; + + @Before + public void setUp() { + bcFipsConfig = new BCFipsConfig(); + } + + @Test + public void testCfbEncryptDecrypt() throws GeneralSecurityException { + SecretKey key = bcFipsConfig.generateKey(); + String passwordStr = "test!@#$%^&*()1234567890"; + byte[] plaintext = passwordStr.getBytes(); + + byte[][] ivCiphertext = bcFipsConfig.cfbEncrypt(key, plaintext); + + byte[] iv = ivCiphertext[0]; + assertNotNull(iv); + byte[] ciphertext = ivCiphertext[1]; + assertNotNull(ciphertext); + + byte[] derivedPlainText = bcFipsConfig.cfbDecrypt(key, iv, ciphertext); + assertNotNull(derivedPlainText); + assertArrayEquals(plaintext, derivedPlainText); + } + +} \ No newline at end of file diff --git a/egantt/pom.xml b/egantt/pom.xml index 58a2a55..b89037c 100644 --- a/egantt/pom.xml +++ b/egantt/pom.xml @@ -57,8 +57,8 @@ org.apache.maven.plugins maven-compiler-plugin - 1.8 - 1.8 + 11 + 11 diff --git a/jfreechart-fse/pom.xml b/jfreechart-fse/pom.xml index 2009ee5..709fa62 100644 --- a/jfreechart-fse/pom.xml +++ b/jfreechart-fse/pom.xml @@ -65,8 +65,8 @@ org.apache.maven.plugins maven-compiler-plugin - 1.8 - 1.8 + 11 + 11 diff --git a/jfreechart-fse/src/main/java/org/jfree/chart/demo/package-info.java b/jfreechart-fse/src/main/java/org/jfree/chart/demo/package-info.java index eb6a29b..f4eea73 100644 --- a/jfreechart-fse/src/main/java/org/jfree/chart/demo/package-info.java +++ b/jfreechart-fse/src/main/java/org/jfree/chart/demo/package-info.java @@ -3,7 +3,7 @@ * available for download (including source code) with the JFreeChart Developer * Guide. For more information, see: *

- * + * * http://www.object-refinery.com/jfreechart/guide.html * */ diff --git a/jfreechart-fse/src/main/java/org/jfree/chart/servlet/ChartDeleter.java b/jfreechart-fse/src/main/java/org/jfree/chart/servlet/ChartDeleter.java deleted file mode 100644 index e79fcc6..0000000 --- a/jfreechart-fse/src/main/java/org/jfree/chart/servlet/ChartDeleter.java +++ /dev/null @@ -1,119 +0,0 @@ -/* =========================================================== - * JFreeChart : a free chart library for the Java(tm) platform - * =========================================================== - * - * (C) Copyright 2000-2012, by Object Refinery Limited and Contributors. - * - * Project Info: http://www.jfree.org/jfreechart/index.html - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or - * (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - * USA. - * - * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. - * Other names may be trademarks of their respective owners.] - * - * ----------------- - * ChartDeleter.java - * ----------------- - * (C) Copyright 2002-2012, by Richard Atkinson and Contributors. - * - * Original Author: Richard Atkinson; - * Contributor(s): -; - * - * Changes - * ------- - * 19-Aug-2002 : Version 1; - * 17-Oct-2002 : Fixed errors reported by Checkstyle (DG); - * ------------- JFREECHART 1.0.x --------------------------------------------- - * 02-Feb-2007 : Removed author tags all over JFreeChart sources (DG); - * - */ - -package org.jfree.chart.servlet; - -import java.io.File; -import java.io.Serializable; -import java.util.List; - -import javax.servlet.http.HttpSessionBindingEvent; -import javax.servlet.http.HttpSessionBindingListener; - -/** - * Used for deleting charts from the temporary directory when the users session - * expires. - */ -public class ChartDeleter implements HttpSessionBindingListener, Serializable { - - /** The chart names. */ - private List chartNames = new java.util.ArrayList(); - - /** - * Blank constructor. - */ - public ChartDeleter() { - super(); - } - - /** - * Add a chart to be deleted when the session expires - * - * @param filename the name of the chart in the temporary directory to be - * deleted. - */ - public void addChart(String filename) { - this.chartNames.add(filename); - } - - /** - * Checks to see if a chart is in the list of charts to be deleted - * - * @param filename the name of the chart in the temporary directory. - * - * @return A boolean value indicating whether the chart is present in the - * list. - */ - public boolean isChartAvailable(String filename) { - return (this.chartNames.contains(filename)); - } - - /** - * Binding this object to the session has no additional effects. - * - * @param event the session bind event. - */ - @Override - public void valueBound(HttpSessionBindingEvent event) { - // do nothing - } - - /** - * When this object is unbound from the session (including upon session - * expiry) the files that have been added to the ArrayList are iterated - * and deleted. - * - * @param event the session unbind event. - */ - @Override - public void valueUnbound(HttpSessionBindingEvent event) { - File tempDir = new File(System.getProperty("java.io.tmpdir")); - for (String filename : this.chartNames) { - File file = new File(tempDir, filename); - if (file.exists()) { - file.delete(); - } - } - } - -} diff --git a/jfreechart-fse/src/main/java/org/jfree/chart/servlet/DisplayChart.java b/jfreechart-fse/src/main/java/org/jfree/chart/servlet/DisplayChart.java deleted file mode 100644 index 088f3d3..0000000 --- a/jfreechart-fse/src/main/java/org/jfree/chart/servlet/DisplayChart.java +++ /dev/null @@ -1,156 +0,0 @@ -/* =========================================================== - * JFreeChart : a free chart library for the Java(tm) platform - * =========================================================== - * - * (C) Copyright 2000-2012, by Object Refinery Limited and Contributors. - * - * Project Info: http://www.jfree.org/jfreechart/index.html - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or - * (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - * USA. - * - * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. - * Other names may be trademarks of their respective owners.] - * - * ----------------- - * DisplayChart.java - * ----------------- - * (C) Copyright 2002-2008, by Richard Atkinson and Contributors. - * - * Original Author: Richard Atkinson; - * Contributor(s): David Gilbert (for Object Refinery Limited); - * - * Changes - * ------- - * 19-Aug-2002 : Version 1; - * 09-Mar-2005 : Added facility to serve up "one time" charts - see - * ServletUtilities.java (DG); - * ------------- JFREECHART 1.0.x --------------------------------------------- - * 02-Feb-2007 : Removed author tags all over JFreeChart sources (DG); - * 03-Dec-2011 : Fixed path disclosure vulnerability - see bug 2879650 (DG); - * - */ - -package org.jfree.chart.servlet; - -import java.io.File; -import java.io.IOException; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import javax.servlet.http.HttpSession; - -/** - * Servlet used for streaming charts to the client browser from the temporary - * directory. You need to add this servlet and mapping to your deployment - * descriptor (web.xml) in order to get it to work. The syntax is as follows: - *

- * <servlet> - * <servlet-name>DisplayChart</servlet-name> - * <servlet-class>org.jfree.chart.servlet.DisplayChart</servlet-class> - * </servlet> - * <servlet-mapping> - * <servlet-name>DisplayChart</servlet-name> - * <url-pattern>/servlet/DisplayChart</url-pattern> - * </servlet-mapping> - * - */ -public class DisplayChart extends HttpServlet { - - /** - * Default constructor. - */ - public DisplayChart() { - super(); - } - - /** - * Init method. - * - * @throws ServletException never. - */ - @Override - public void init() throws ServletException { - // do nothing - } - - /** - * Service method. - * - * @param request the request. - * @param response the response. - * - * @throws ServletException ??. - * @throws IOException ??. - */ - @Override - public void service(HttpServletRequest request, - HttpServletResponse response) throws ServletException, IOException { - - HttpSession session = request.getSession(); - String filename = request.getParameter("filename"); - - if (filename == null) { - throw new ServletException("Parameter 'filename' must be supplied"); - } - - // Replace ".." with "" - // This is to prevent access to the rest of the file system - filename = ServletUtilities.searchReplace(filename, "..", ""); - - // Check the file exists - File file = new File(System.getProperty("java.io.tmpdir"), filename); - if (!file.exists()) { - throw new ServletException( - "Unable to display the chart with the filename '" - + filename + "'."); - } - - // Check that the graph being served was created by the current user - // or that it begins with "public" - boolean isChartInUserList = false; - ChartDeleter chartDeleter = (ChartDeleter) session.getAttribute( - "JFreeChart_Deleter"); - if (chartDeleter != null) { - isChartInUserList = chartDeleter.isChartAvailable(filename); - } - - boolean isChartPublic = false; - if (filename.length() >= 6) { - if (filename.substring(0, 6).equals("public")) { - isChartPublic = true; - } - } - - boolean isOneTimeChart = false; - if (filename.startsWith(ServletUtilities.getTempOneTimeFilePrefix())) { - isOneTimeChart = true; - } - - if (isChartInUserList || isChartPublic || isOneTimeChart) { - // Serve it up - ServletUtilities.sendTempFile(file, response); - if (isOneTimeChart) { - file.delete(); - } - } - else { - throw new ServletException("Chart image not found"); - } - } - -} diff --git a/jfreechart-fse/src/main/java/org/jfree/chart/servlet/ServletUtilities.java b/jfreechart-fse/src/main/java/org/jfree/chart/servlet/ServletUtilities.java deleted file mode 100644 index 409d3b1..0000000 --- a/jfreechart-fse/src/main/java/org/jfree/chart/servlet/ServletUtilities.java +++ /dev/null @@ -1,439 +0,0 @@ -/* =========================================================== - * JFreeChart : a free chart library for the Java(tm) platform - * =========================================================== - * - * (C) Copyright 2000-2012, by Object Refinery Limited and Contributors. - * - * Project Info: http://www.jfree.org/jfreechart/index.html - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or - * (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - * USA. - * - * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. - * Other names may be trademarks of their respective owners.] - * - * --------------------- - * ServletUtilities.java - * --------------------- - * (C) Copyright 2002-2008, by Richard Atkinson and Contributors. - * - * Original Author: Richard Atkinson; - * Contributor(s): J?rgen Hoffman; - * David Gilbert (for Object Refinery Limited); - * Douglas Clayton; - * - * Changes - * ------- - * 19-Aug-2002 : Version 1; - * 20-Apr-2003 : Added additional sendTempFile method to allow MIME type - * specification and modified original sendTempFile method to - * automatically set MIME type for JPEG and PNG files - * 23-Jun-2003 : Added additional sendTempFile method at the request of - * J?rgen Hoffman; - * 07-Jul-2003 : Added more header information to streamed images; - * 19-Aug-2003 : Forced images to be stored in the temporary directory defined - * by System property java.io.tmpdir, rather than default (RA); - * 24-Mar-2004 : Added temp filename prefix attribute (DG); - * 09-Mar-2005 : Added "one time" file option (DG); - * ------------- JFREECHART 1.0.x RELEASED ------------------------------------ - * 10-Jan-2006 : Updated API docs and reformatted (DG); - * 13-Sep-2006 : Format date in response header in English, not locale default - * (see bug 1557141) (DG); - * - */ - -package org.jfree.chart.servlet; - -import java.io.BufferedInputStream; -import java.io.BufferedOutputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.Locale; -import java.util.TimeZone; - -import javax.servlet.http.HttpServletResponse; -import javax.servlet.http.HttpSession; - -import org.jfree.chart.ChartRenderingInfo; -import org.jfree.chart.ChartUtilities; -import org.jfree.chart.JFreeChart; - -/** - * Utility class used for servlet related JFreeChart operations. - */ -public class ServletUtilities { - - /** The filename prefix. */ - private static String tempFilePrefix = "jfreechart-"; - - /** A prefix for "one time" charts. */ - private static String tempOneTimeFilePrefix = "jfreechart-onetime-"; - - /** - * Returns the prefix for the temporary file names generated by this class. - * - * @return The prefix (never null). - */ - public static String getTempFilePrefix() { - return ServletUtilities.tempFilePrefix; - } - - /** - * Sets the prefix for the temporary file names generated by this class. - * - * @param prefix the prefix (null not permitted). - */ - public static void setTempFilePrefix(String prefix) { - if (prefix == null) { - throw new IllegalArgumentException("Null 'prefix' argument."); - } - ServletUtilities.tempFilePrefix = prefix; - } - - /** - * Returns the prefix for "one time" temporary file names generated by - * this class. - * - * @return The prefix. - */ - public static String getTempOneTimeFilePrefix() { - return ServletUtilities.tempOneTimeFilePrefix; - } - - /** - * Sets the prefix for the "one time" temporary file names generated by - * this class. - * - * @param prefix the prefix (null not permitted). - */ - public static void setTempOneTimeFilePrefix(String prefix) { - if (prefix == null) { - throw new IllegalArgumentException("Null 'prefix' argument."); - } - ServletUtilities.tempOneTimeFilePrefix = prefix; - } - - /** - * Saves the chart as a PNG format file in the temporary directory. - * - * @param chart the JFreeChart to be saved. - * @param width the width of the chart. - * @param height the height of the chart. - * @param session the HttpSession of the client (if null, the - * temporary file is marked as "one-time" and deleted by - * the {@link DisplayChart} servlet right after it is - * streamed to the client). - * - * @return The filename of the chart saved in the temporary directory. - * - * @throws IOException if there is a problem saving the file. - */ - public static String saveChartAsPNG(JFreeChart chart, int width, int height, - HttpSession session) throws IOException { - - return ServletUtilities.saveChartAsPNG(chart, width, height, null, - session); - - } - - /** - * Saves the chart as a PNG format file in the temporary directory and - * populates the {@link ChartRenderingInfo} object which can be used to - * generate an HTML image map. - * - * @param chart the chart to be saved (null not permitted). - * @param width the width of the chart. - * @param height the height of the chart. - * @param info the ChartRenderingInfo object to be populated - * (null permitted). - * @param session the HttpSession of the client (if null, the - * temporary file is marked as "one-time" and deleted by - * the {@link DisplayChart} servlet right after it is - * streamed to the client). - * - * @return The filename of the chart saved in the temporary directory. - * - * @throws IOException if there is a problem saving the file. - */ - public static String saveChartAsPNG(JFreeChart chart, int width, int height, - ChartRenderingInfo info, HttpSession session) throws IOException { - - if (chart == null) { - throw new IllegalArgumentException("Null 'chart' argument."); - } - ServletUtilities.createTempDir(); - String prefix = ServletUtilities.tempFilePrefix; - if (session == null) { - prefix = ServletUtilities.tempOneTimeFilePrefix; - } - File tempFile = File.createTempFile(prefix, ".png", - new File(System.getProperty("java.io.tmpdir"))); - ChartUtilities.saveChartAsPNG(tempFile, chart, width, height, info); - if (session != null) { - ServletUtilities.registerChartForDeletion(tempFile, session); - } - return tempFile.getName(); - - } - - /** - * Saves the chart as a JPEG format file in the temporary directory. - *

- * SPECIAL NOTE: Please avoid using JPEG as an image format for charts, - * it is a "lossy" format that introduces visible distortions in the - * resulting image - use PNG instead. In addition, note that JPEG output - * is supported by JFreeChart only for JRE 1.4.2 or later. - * - * @param chart the JFreeChart to be saved. - * @param width the width of the chart. - * @param height the height of the chart. - * @param session the HttpSession of the client (if null, the - * temporary file is marked as "one-time" and deleted by - * the {@link DisplayChart} servlet right after it is - * streamed to the client). - * - * @return The filename of the chart saved in the temporary directory. - * - * @throws IOException if there is a problem saving the file. - */ - public static String saveChartAsJPEG(JFreeChart chart, int width, - int height, HttpSession session) - throws IOException { - - return ServletUtilities.saveChartAsJPEG(chart, width, height, null, - session); - - } - - /** - * Saves the chart as a JPEG format file in the temporary directory and - * populates the ChartRenderingInfo object which can be used - * to generate an HTML image map. - *

- * SPECIAL NOTE: Please avoid using JPEG as an image format for charts, - * it is a "lossy" format that introduces visible distortions in the - * resulting image - use PNG instead. In addition, note that JPEG output - * is supported by JFreeChart only for JRE 1.4.2 or later. - * - * @param chart the chart to be saved (null not permitted). - * @param width the width of the chart - * @param height the height of the chart - * @param info the ChartRenderingInfo object to be populated - * @param session the HttpSession of the client (if null, the - * temporary file is marked as "one-time" and deleted by - * the {@link DisplayChart} servlet right after it is - * streamed to the client). - * - * @return The filename of the chart saved in the temporary directory - * - * @throws IOException if there is a problem saving the file. - */ - public static String saveChartAsJPEG(JFreeChart chart, int width, - int height, ChartRenderingInfo info, HttpSession session) - throws IOException { - - if (chart == null) { - throw new IllegalArgumentException("Null 'chart' argument."); - } - - ServletUtilities.createTempDir(); - String prefix = ServletUtilities.tempFilePrefix; - if (session == null) { - prefix = ServletUtilities.tempOneTimeFilePrefix; - } - File tempFile = File.createTempFile(prefix, ".jpeg", - new File(System.getProperty("java.io.tmpdir"))); - ChartUtilities.saveChartAsJPEG(tempFile, chart, width, height, info); - if (session != null) { - ServletUtilities.registerChartForDeletion(tempFile, session); - } - return tempFile.getName(); - - } - - /** - * Creates the temporary directory if it does not exist. Throws a - * RuntimeException if the temporary directory is - * null. Uses the system property java.io.tmpdir - * as the temporary directory. This sounds like a strange thing to do but - * my temporary directory was not created on my default Tomcat 4.0.3 - * installation. Could save some questions on the forum if it is created - * when not present. - */ - protected static void createTempDir() { - String tempDirName = System.getProperty("java.io.tmpdir"); - if (tempDirName == null) { - throw new RuntimeException("Temporary directory system property " - + "(java.io.tmpdir) is null."); - } - - // create the temporary directory if it doesn't exist - File tempDir = new File(tempDirName); - if (!tempDir.exists()) { - tempDir.mkdirs(); - } - } - - /** - * Adds a {@link ChartDeleter} object to the session object with the name - * JFreeChart_Deleter if there is not already one bound to the - * session and adds the filename to the list of charts to be deleted. - * - * @param tempFile the file to be deleted. - * @param session the HTTP session of the client. - */ - protected static void registerChartForDeletion(File tempFile, - HttpSession session) { - - // Add chart to deletion list in session - if (session != null) { - ChartDeleter chartDeleter - = (ChartDeleter) session.getAttribute("JFreeChart_Deleter"); - if (chartDeleter == null) { - chartDeleter = new ChartDeleter(); - session.setAttribute("JFreeChart_Deleter", chartDeleter); - } - chartDeleter.addChart(tempFile.getName()); - } - else { - System.out.println("Session is null - chart will not be deleted"); - } - } - - /** - * Binary streams the specified file in the temporary directory to the - * HTTP response in 1KB chunks. - * - * @param filename the name of the file in the temporary directory. - * @param response the HTTP response object. - * - * @throws IOException if there is an I/O problem. - */ - public static void sendTempFile(String filename, - HttpServletResponse response) throws IOException { - - File file = new File(System.getProperty("java.io.tmpdir"), filename); - ServletUtilities.sendTempFile(file, response); - } - - /** - * Binary streams the specified file to the HTTP response in 1KB chunks. - * - * @param file the file to be streamed. - * @param response the HTTP response object. - * - * @throws IOException if there is an I/O problem. - */ - public static void sendTempFile(File file, HttpServletResponse response) - throws IOException { - - String mimeType = null; - String filename = file.getName(); - if (filename.length() > 5) { - if (filename.substring(filename.length() - 5, - filename.length()).equals(".jpeg")) { - mimeType = "image/jpeg"; - } - else if (filename.substring(filename.length() - 4, - filename.length()).equals(".png")) { - mimeType = "image/png"; - } - } - ServletUtilities.sendTempFile(file, response, mimeType); - } - - /** - * Binary streams the specified file to the HTTP response in 1KB chunks. - * - * @param file the file to be streamed. - * @param response the HTTP response object. - * @param mimeType the mime type of the file, null allowed. - * - * @throws IOException if there is an I/O problem. - */ - public static void sendTempFile(File file, HttpServletResponse response, - String mimeType) throws IOException { - - if (file.exists()) { - BufferedInputStream bis = new BufferedInputStream( - new FileInputStream(file)); - - // Set HTTP headers - if (mimeType != null) { - response.setHeader("Content-Type", mimeType); - } - response.setHeader("Content-Length", String.valueOf(file.length())); - SimpleDateFormat sdf = new SimpleDateFormat( - "EEE, dd MMM yyyy HH:mm:ss z", Locale.ENGLISH); - sdf.setTimeZone(TimeZone.getTimeZone("GMT")); - response.setHeader("Last-Modified", - sdf.format(new Date(file.lastModified()))); - - BufferedOutputStream bos = new BufferedOutputStream( - response.getOutputStream()); - byte[] input = new byte[1024]; - boolean eof = false; - while (!eof) { - int length = bis.read(input); - if (length == -1) { - eof = true; - } - else { - bos.write(input, 0, length); - } - } - bos.flush(); - bis.close(); - bos.close(); - } - else { - throw new FileNotFoundException(file.getAbsolutePath()); - } - } - - /** - * Perform a search/replace operation on a String - * There are String methods to do this since (JDK 1.4) - * - * @param inputString the String to have the search/replace operation. - * @param searchString the search String. - * @param replaceString the replace String. - * - * @return The String with the replacements made. - */ - public static String searchReplace(String inputString, - String searchString, - String replaceString) { - - int i = inputString.indexOf(searchString); - if (i == -1) { - return inputString; - } - - String r = ""; - r += inputString.substring(0, i) + replaceString; - if (i + searchString.length() < inputString.length()) { - r += searchReplace(inputString.substring(i + searchString.length()), - searchString, replaceString); - } - - return r; - } - -} diff --git a/jfreechart-fse/src/main/java/org/jfree/chart/servlet/package-info.java b/jfreechart-fse/src/main/java/org/jfree/chart/servlet/package-info.java deleted file mode 100644 index 89d17de..0000000 --- a/jfreechart-fse/src/main/java/org/jfree/chart/servlet/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * Classes for providing useful servlet and JSP functionality. - */ -package org.jfree.chart.servlet; diff --git a/jfreechart-fse/src/main/java/org/jfree/data/jdbc/JDBCCategoryDataset.java b/jfreechart-fse/src/main/java/org/jfree/data/jdbc/JDBCCategoryDataset.java index cde2927..3848f2e 100644 --- a/jfreechart-fse/src/main/java/org/jfree/data/jdbc/JDBCCategoryDataset.java +++ b/jfreechart-fse/src/main/java/org/jfree/data/jdbc/JDBCCategoryDataset.java @@ -66,12 +66,11 @@ import java.sql.Connection; import java.sql.Date; import java.sql.DriverManager; +import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; -import java.sql.Statement; import java.sql.Types; - import org.jfree.data.category.CategoryDataset; import org.jfree.data.category.DefaultCategoryDataset; @@ -206,11 +205,11 @@ public void executeQuery(String query) throws SQLException { */ public void executeQuery(Connection con, String query) throws SQLException { - Statement statement = null; + PreparedStatement statement = null; ResultSet resultSet = null; try { - statement = con.createStatement(); - resultSet = statement.executeQuery(query); + statement = con.prepareStatement(query); + resultSet = statement.executeQuery(); ResultSetMetaData metaData = resultSet.getMetaData(); int columnCount = metaData.getColumnCount(); diff --git a/jfreechart-fse/src/main/java/org/jfree/data/jdbc/JDBCPieDataset.java b/jfreechart-fse/src/main/java/org/jfree/data/jdbc/JDBCPieDataset.java index ae01674..8a6b176 100644 --- a/jfreechart-fse/src/main/java/org/jfree/data/jdbc/JDBCPieDataset.java +++ b/jfreechart-fse/src/main/java/org/jfree/data/jdbc/JDBCPieDataset.java @@ -60,13 +60,12 @@ import java.sql.Connection; import java.sql.DriverManager; +import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; -import java.sql.Statement; import java.sql.Timestamp; import java.sql.Types; - import org.jfree.data.general.DefaultPieDataset; import org.jfree.data.general.PieDataset; @@ -166,12 +165,12 @@ public void executeQuery(String query) throws SQLException { */ public void executeQuery(Connection con, String query) throws SQLException { - Statement statement = null; + PreparedStatement statement = null; ResultSet resultSet = null; try { - statement = con.createStatement(); - resultSet = statement.executeQuery(query); + statement = con.prepareStatement(query); + resultSet = statement.executeQuery(); ResultSetMetaData metaData = resultSet.getMetaData(); int columnCount = metaData.getColumnCount(); diff --git a/jfreechart-fse/src/main/java/org/jfree/data/jdbc/JDBCXYDataset.java b/jfreechart-fse/src/main/java/org/jfree/data/jdbc/JDBCXYDataset.java index 4534e33..0fa1c30 100644 --- a/jfreechart-fse/src/main/java/org/jfree/data/jdbc/JDBCXYDataset.java +++ b/jfreechart-fse/src/main/java/org/jfree/data/jdbc/JDBCXYDataset.java @@ -74,15 +74,14 @@ import java.sql.Connection; import java.sql.DriverManager; +import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; -import java.sql.Statement; import java.sql.Types; import java.util.ArrayList; import java.util.Date; import java.util.List; - import org.jfree.data.Range; import org.jfree.data.RangeInfo; import org.jfree.data.general.Dataset; @@ -237,10 +236,10 @@ public void executeQuery(Connection con, String query) } ResultSet resultSet = null; - Statement statement = null; + PreparedStatement statement = null; try { - statement = con.createStatement(); - resultSet = statement.executeQuery(query); + statement = con.prepareStatement(query); + resultSet = statement.executeQuery(); ResultSetMetaData metaData = resultSet.getMetaData(); int numberOfColumns = metaData.getColumnCount(); diff --git a/jfreechart-fse/src/main/java/overview.html b/jfreechart-fse/src/main/java/overview.html index 4fde26d..ffdc0c2 100644 --- a/jfreechart-fse/src/main/java/overview.html +++ b/jfreechart-fse/src/main/java/overview.html @@ -4,7 +4,7 @@ JFreeChart is a free chart library for Java that can generate a wide variety of charts for use in applications, applets and servlets.

-Please visit +Please visit http://www.jfree.org/jfreechart/index.html for the latest information about JFreeChart. diff --git a/pom.xml b/pom.xml index b6ed24b..99da429 100644 --- a/pom.xml +++ b/pom.xml @@ -29,7 +29,7 @@ org.projectlombok lombok - 1.18.12 + 1.18.24 org.slf4j @@ -102,13 +102,32 @@ org.yaml snakeyaml - 1.26 + 1.33 org.apache.maven.plugins maven-toolchains-plugin 3.0.0 + + + org.bouncycastle + bc-fips + 1.0.2.3 + + + + com.github.peter-gergely-horvath + windpapi4j + 1.0 + + + + net.java.dev.jna + jna + 5.10.0 + + @@ -152,8 +171,8 @@ maven-compiler-plugin 3.8.1 - 1.8 - 1.8 + 11 + 11 com.google.dagger @@ -163,7 +182,7 @@ org.projectlombok lombok - 1.18.16 + 1.18.24 com.google.auto.factory @@ -180,27 +199,6 @@ 3.1.1 - - - org.apache.maven.plugins - maven-toolchains-plugin - 3.0.0 - - - - toolchain - - - - - - - 1.8 - oracle - - - -