diff --git a/pom.xml b/pom.xml
index 68830f0..f07a6ad 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
com.bwssystems.HABridge
ha-bridge
- 4.3.1Secure-b
+ 4.3.1Secure-c
jar
HA Bridge
@@ -63,7 +63,7 @@
com.sparkjava
spark-core
- 2.3
+ 2.5.5
slf4j-simple
diff --git a/src/main/java/com/bwssystems/HABridge/AuthFramework.java b/src/main/java/com/bwssystems/HABridge/AuthFramework.java
new file mode 100644
index 0000000..543b424
--- /dev/null
+++ b/src/main/java/com/bwssystems/HABridge/AuthFramework.java
@@ -0,0 +1,25 @@
+package com.bwssystems.HABridge;
+
+import spark.Request;
+
+public abstract class AuthFramework {
+ private static final String USER_SESSION_ID = "user";
+
+ public AuthFramework() {
+ // TODO Auto-generated constructor stub
+ }
+
+ private void addAuthenticatedUser(Request request, User u) {
+ request.session().attribute(USER_SESSION_ID, u);
+
+ }
+
+ private void removeAuthenticatedUser(Request request) {
+ request.session().removeAttribute(USER_SESSION_ID);
+
+ }
+
+ private User getAuthenticatedUser(Request request) {
+ return request.session().attribute(USER_SESSION_ID);
+ }
+}
diff --git a/src/main/java/com/bwssystems/HABridge/BridgeSecurity.java b/src/main/java/com/bwssystems/HABridge/BridgeSecurity.java
index fca261c..3d13c59 100644
--- a/src/main/java/com/bwssystems/HABridge/BridgeSecurity.java
+++ b/src/main/java/com/bwssystems/HABridge/BridgeSecurity.java
@@ -25,10 +25,12 @@ public class BridgeSecurity {
(byte) 0xde, (byte) 0x33, (byte) 0x10, (byte) 0x12,
};
private BridgeSecurityDescriptor securityDescriptor;
+ private boolean settingsChanged;
public BridgeSecurity(char[] theKey, String theData) {
habridgeKey = theKey;
securityDescriptor = null;
+ settingsChanged = false;
String anError = null;
if(theData != null && !theData.isEmpty()) {
try {
@@ -56,20 +58,28 @@ public class BridgeSecurity {
return securityDescriptor.isUseLinkButton();
}
- public void setPassword(String aPassword) throws IOException {
- if(aPassword != null) {
- securityDescriptor.setUiPassword(String.valueOf(base64Decode(aPassword)));
- securityDescriptor.setPasswordSet(true);
- } else {
- securityDescriptor.setUiPassword(null);
- securityDescriptor.setPasswordSet(false);
+ public String setPassword(User aUser) throws IOException {
+ String error = null;
+ if(aUser != null) {
+ error = aUser.validate();
+ if(error == null) {
+ User theUser = securityDescriptor.getUsers().get(aUser.getUsername());
+ if(theUser != null) {
+ theUser.setPassword(aUser.getPassword());
+ theUser.setPassword2(null);
+ settingsChanged = true;
+ }
+ }
}
- securityDescriptor.setSettingsChanged(true);
+ else
+ error = "invalid user object given";
+
+ return error;
}
public void setExecGarden(String theGarden) {
securityDescriptor.setExecGarden(theGarden);
- securityDescriptor.setSettingsChanged(true);
+ settingsChanged = true;
}
public String getExecGarden() {
@@ -77,22 +87,43 @@ public class BridgeSecurity {
}
public void setUseLinkButton(boolean useThis) {
securityDescriptor.setUseLinkButton(useThis);
- securityDescriptor.setSettingsChanged(true);
+ settingsChanged = true;
}
- public boolean validatePassword(String targetPassword) throws IOException {
- if(securityDescriptor.isPasswordSet()) {
- if(securityDescriptor.getUiPassword().equals(String.valueOf(base64Decode(targetPassword))))
+ public SecurityInfo getSecurityInfo() {
+ SecurityInfo theInfo = new SecurityInfo();
+ theInfo.setExecGarden(getExecGarden());
+ theInfo.setUseLinkButton(isUseLinkButton());
+ theInfo.setSecure(isSecure());
+ return theInfo;
+ }
+ public boolean validatePassword(User targetUser) throws IOException {
+ if(targetUser != null) {
+ User theUser = securityDescriptor.getUsers().get(targetUser.getUsername());
+ if(theUser.getPassword() != null) {
+ theUser.setPassword2(targetUser.getPassword());
+ if(theUser.validatePassword()) {
+ theUser.setPassword2(null);
+ return true;
+ }
+ } else {
+ log.warn("validating password when password is not set....");
return true;
- } else {
- log.warn("validating password when password is not set....");
- return true;
+ }
}
return false;
}
public boolean isSecure() {
- return securityDescriptor.isPasswordSet();
+ return securityDescriptor.isSecure();
+ }
+
+ public boolean isSettingsChanged() {
+ return settingsChanged;
+ }
+
+ public void setSettingsChanged(boolean settingsChanged) {
+ this.settingsChanged = settingsChanged;
}
private String encrypt(String property) throws GeneralSecurityException, UnsupportedEncodingException {
diff --git a/src/main/java/com/bwssystems/HABridge/BridgeSecurityDescriptor.java b/src/main/java/com/bwssystems/HABridge/BridgeSecurityDescriptor.java
index 0288d3a..380f46a 100644
--- a/src/main/java/com/bwssystems/HABridge/BridgeSecurityDescriptor.java
+++ b/src/main/java/com/bwssystems/HABridge/BridgeSecurityDescriptor.java
@@ -1,33 +1,24 @@
package com.bwssystems.HABridge;
+import java.util.Map;
+
public class BridgeSecurityDescriptor {
- private String uiPassword;
- private boolean passwordSet;
+ private Map users;
private boolean useLinkButton;
private String execGarden;
- private boolean settingsChanged;
+ private boolean secureHueApi;
public BridgeSecurityDescriptor() {
super();
- this.setUiPassword(null);
- this.setPasswordSet(false);
this.setUseLinkButton(false);
}
- public String getUiPassword() {
- return uiPassword;
+ public Map getUsers() {
+ return users;
}
- public void setUiPassword(String uiPassword) {
- this.uiPassword = uiPassword;
- }
-
- public boolean isPasswordSet() {
- return passwordSet;
- }
-
- public void setPasswordSet(boolean passwordSet) {
- this.passwordSet = passwordSet;
+ public void setUsers(Map users) {
+ this.users = users;
}
public boolean isUseLinkButton() {
@@ -46,11 +37,26 @@ public class BridgeSecurityDescriptor {
this.execGarden = execGarden;
}
- public boolean isSettingsChanged() {
- return settingsChanged;
+ public boolean isSecureHueApi() {
+ return secureHueApi;
}
- public void setSettingsChanged(boolean settingsChanged) {
- this.settingsChanged = settingsChanged;
+ public void setSecureHueApi(boolean secureHueApi) {
+ this.secureHueApi = secureHueApi;
+ }
+
+ public boolean isSecure() {
+ boolean secureFlag = false;
+ if(users != null && !users.isEmpty()) {
+ for (Map.Entry entry : users.entrySet())
+ {
+ if(entry.getValue().getPassword() != null && !entry.getValue().getPassword().isEmpty()) {
+ secureFlag = true;
+ break;
+ }
+ }
+ }
+ return secureFlag;
+
}
}
diff --git a/src/main/java/com/bwssystems/HABridge/BridgeSettings.java b/src/main/java/com/bwssystems/HABridge/BridgeSettings.java
index 1028712..54d1f16 100644
--- a/src/main/java/com/bwssystems/HABridge/BridgeSettings.java
+++ b/src/main/java/com/bwssystems/HABridge/BridgeSettings.java
@@ -1,6 +1,7 @@
package com.bwssystems.HABridge;
import java.io.IOException;
+import java.io.UnsupportedEncodingException;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
@@ -10,6 +11,7 @@ import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.nio.file.attribute.PosixFilePermission;
+import java.security.GeneralSecurityException;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Set;
@@ -213,6 +215,18 @@ public class BridgeSettings extends BackupHandler {
log.debug("Save HA Bridge settings.");
Path configPath = Paths.get(theBridgeSettings.getConfigfile());
JsonTransformer aRenderer = new JsonTransformer();
+ if(bridgeSecurity.isSettingsChanged()) {
+ try {
+ newBridgeSettings.setSecurityData(bridgeSecurity.getSecurityDescriptorData());
+ } catch (UnsupportedEncodingException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (GeneralSecurityException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ bridgeSecurity.setSettingsChanged(false);
+ }
String jsonValue = aRenderer.render(newBridgeSettings);
configWriter(jsonValue, configPath);
_loadConfig(configPath);
diff --git a/src/main/java/com/bwssystems/HABridge/SecurityInfo.java b/src/main/java/com/bwssystems/HABridge/SecurityInfo.java
new file mode 100644
index 0000000..8c7ce9b
--- /dev/null
+++ b/src/main/java/com/bwssystems/HABridge/SecurityInfo.java
@@ -0,0 +1,33 @@
+package com.bwssystems.HABridge;
+
+public class SecurityInfo {
+ private boolean useLinkButton;
+ private String execGarden;
+ private boolean seucreHueApi;
+ private boolean isSecure;
+
+ public boolean isUseLinkButton() {
+ return useLinkButton;
+ }
+ public void setUseLinkButton(boolean useLinkButton) {
+ this.useLinkButton = useLinkButton;
+ }
+ public String getExecGarden() {
+ return execGarden;
+ }
+ public void setExecGarden(String execGarden) {
+ this.execGarden = execGarden;
+ }
+ public boolean isSeucreHueApi() {
+ return seucreHueApi;
+ }
+ public void setSeucreHueApi(boolean seucreHueApi) {
+ this.seucreHueApi = seucreHueApi;
+ }
+ public boolean isSecure() {
+ return isSecure;
+ }
+ public void setSecure(boolean isSecure) {
+ this.isSecure = isSecure;
+ }
+}
diff --git a/src/main/java/com/bwssystems/HABridge/SystemControl.java b/src/main/java/com/bwssystems/HABridge/SystemControl.java
index 0cdd456..38e69b6 100644
--- a/src/main/java/com/bwssystems/HABridge/SystemControl.java
+++ b/src/main/java/com/bwssystems/HABridge/SystemControl.java
@@ -31,7 +31,7 @@ import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.classic.spi.LoggingEvent;
import ch.qos.logback.core.read.CyclicBufferAppender;
-public class SystemControl {
+public class SystemControl extends AuthFramework {
private static final Logger log = LoggerFactory.getLogger(SystemControl.class);
public static final String CYCLIC_BUFFER_APPENDER_NAME = "CYCLIC";
private LoggerContext lc;
@@ -110,6 +110,13 @@ public class SystemControl {
return theLogServiceMgr.getConfiguredLoggers();
}, new JsonTransformer());
+ // http://ip_address:port/system/securityinfo gets the security info for the bridge
+ get (SYSTEM_CONTEXT + "/securityinfo", "application/json", (request, response) -> {
+ log.debug("Get security info");
+ response.status(200);
+ return bridgeSettings.getBridgeSecurity().getSecurityInfo();
+ }, new JsonTransformer());
+
// http://ip_address:port/system/presslinkbutton CORS request
options(SYSTEM_CONTEXT + "/presslinkbutton", "application/json", (request, response) -> {
response.status(HttpStatus.SC_OK);
@@ -128,6 +135,55 @@ public class SystemControl {
return null;
}, new JsonTransformer());
+// http://ip_address:port/system/setpassword CORS request
+ options(SYSTEM_CONTEXT + "/setpassword", "application/json", (request, response) -> {
+ response.status(HttpStatus.SC_OK);
+ response.header("Access-Control-Allow-Origin", request.headers("Origin"));
+ response.header("Access-Control-Allow-Methods", "GET, POST, PUT");
+ response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
+ response.header("Content-Type", "text/html; charset=utf-8");
+ return "";
+ });
+// http://ip_address:port/system/setpassword which sets a password for a given user
+ post(SYSTEM_CONTEXT + "/setpassword", "application/json", (request, response) -> {
+ log.debug("setpassword....");
+ return null;
+ }, new JsonTransformer());
+
+// http://ip_address:port/system/changesecurityinfo CORS request
+ options(SYSTEM_CONTEXT + "/changesecurityinfo", "application/json", (request, response) -> {
+ response.status(HttpStatus.SC_OK);
+ response.header("Access-Control-Allow-Origin", request.headers("Origin"));
+ response.header("Access-Control-Allow-Methods", "GET, POST, PUT");
+ response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
+ response.header("Content-Type", "text/html; charset=utf-8");
+ return "";
+ });
+// http://ip_address:port/system/changesecurityinfo which sets the security settings other than passwords and users
+ post(SYSTEM_CONTEXT + "/changesecurityinfo", "application/json", (request, response) -> {
+ log.debug("changesecurityinfo....");
+ SecurityInfo theInfo = new Gson().fromJson(request.body(), SecurityInfo.class);
+ if(theInfo.getExecGarden() != null)
+ bridgeSettings.getBridgeSecurity().setExecGarden(theInfo.getExecGarden());
+ bridgeSettings.getBridgeSecurity().setUseLinkButton(theInfo.isUseLinkButton());
+ return null;
+ }, new JsonTransformer());
+
+// http://ip_address:port/system/login CORS request
+ options(SYSTEM_CONTEXT + "/login", "application/json", (request, response) -> {
+ response.status(HttpStatus.SC_OK);
+ response.header("Access-Control-Allow-Origin", request.headers("Origin"));
+ response.header("Access-Control-Allow-Methods", "GET, POST, PUT");
+ response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
+ response.header("Content-Type", "text/html; charset=utf-8");
+ return "";
+ });
+// http://ip_address:port/system/login validates the login
+ post(SYSTEM_CONTEXT + "/login", "application/json", (request, response) -> {
+ log.debug("login....");
+ return null;
+ }, new JsonTransformer());
+
// http://ip_address:port/system/logmgmt/update CORS request
options(SYSTEM_CONTEXT + "/logmgmt/update", "application/json", (request, response) -> {
response.status(HttpStatus.SC_OK);
diff --git a/src/main/java/com/bwssystems/HABridge/User.java b/src/main/java/com/bwssystems/HABridge/User.java
new file mode 100644
index 0000000..a27824e
--- /dev/null
+++ b/src/main/java/com/bwssystems/HABridge/User.java
@@ -0,0 +1,65 @@
+package com.bwssystems.HABridge;
+
+import spark.utils.StringUtils;
+
+public class User {
+ private int id;
+
+ private String username;
+
+ private String password;
+
+ private String password2;
+
+ public int getId() {
+ return id;
+ }
+
+ public void setId(int id) {
+ this.id = id;
+ }
+
+ public String getUsername() {
+ return username;
+ }
+
+ public void setUsername(String username) {
+ this.username = username;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public void setPassword(String password) {
+ this.password = password;
+ }
+
+ public String getPassword2() {
+ return password2;
+ }
+
+ public void setPassword2(String password2) {
+ this.password2 = password2;
+ }
+
+ public String validate() {
+ String error = null;
+
+ if(StringUtils.isEmpty(username)) {
+ error = "You have to enter a username";
+ } else if(StringUtils.isEmpty(password)) {
+ error = "You have to enter a password";
+ } else if(!password.equals(password2)) {
+ error = "The two passwords do not match";
+ }
+
+ return error;
+ }
+
+ public boolean validatePassword() {
+ if(password != null && password2 != null)
+ return password.equals(password2);
+ return false;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/bwssystems/HABridge/hue/HueMulator.java b/src/main/java/com/bwssystems/HABridge/hue/HueMulator.java
index a1d0296..fba75ad 100644
--- a/src/main/java/com/bwssystems/HABridge/hue/HueMulator.java
+++ b/src/main/java/com/bwssystems/HABridge/hue/HueMulator.java
@@ -1,5 +1,6 @@
package com.bwssystems.HABridge.hue;
+import com.bwssystems.HABridge.AuthFramework;
import com.bwssystems.HABridge.BridgeSettings;
import com.bwssystems.HABridge.BridgeSettingsDescriptor;
import com.bwssystems.HABridge.DeviceMapTypes;
@@ -40,7 +41,7 @@ import java.util.Map;
* Based on Armzilla's HueMulator - a Philips Hue emulator using sparkjava rest server
*/
-public class HueMulator {
+public class HueMulator extends AuthFramework {
private static final Logger log = LoggerFactory.getLogger(HueMulator.class);
private static final String HUE_CONTEXT = "/api";
diff --git a/src/main/resources/public/scripts/app.js b/src/main/resources/public/scripts/app.js
index 5898c7f..c12d0a3 100644
--- a/src/main/resources/public/scripts/app.js
+++ b/src/main/resources/public/scripts/app.js
@@ -63,6 +63,7 @@ app.run( function (bridgeService) {
bridgeService.loadBridgeSettings();
bridgeService.getHABridgeVersion();
bridgeService.getTestUser();
+ bridgeService.getSecurityInfo();
bridgeService.viewMapTypes();
});
@@ -80,7 +81,10 @@ String.prototype.replaceAll = function (search, replace)
app.service ('bridgeService', function ($http, $window, ngToast) {
var self = this;
- this.state = {base: "./api/devices", bridgelocation: ".", systemsbase: "./system", huebase: "./api", configs: [], backups: [], devices: [], device: {}, mapandid: [], type: "", settings: [], myToastMsg: [], logMsgs: [], loggerInfo: [], mapTypes: [], olddevicename: "", logShowAll: false, isInControl: false, showVera: false, showHarmony: false, showNest: false, showHue: false, showHal: false, showMqtt: false, showHass: false, showDomoticz: false, showSomfy: false, showLifx: false, habridgeversion: "", viewDevId: "", queueDevId: ""};
+ this.state = {base: "./api/devices", bridgelocation: ".", systemsbase: "./system", huebase: "./api", configs: [], backups: [], devices: [], device: {},
+ mapandid: [], type: "", settings: [], myToastMsg: [], logMsgs: [], loggerInfo: [], mapTypes: [], olddevicename: "", logShowAll: false,
+ isInControl: false, showVera: false, showHarmony: false, showNest: false, showHue: false, showHal: false, showMqtt: false, showHass: false,
+ showDomoticz: false, showSomfy: false, showLifx: false, habridgeversion: "", viewDevId: "", queueDevId: "", securityInfo: {}};
this.displayWarn = function(errorTitle, error) {
var toastContent = errorTitle;
@@ -122,6 +126,12 @@ app.service ('bridgeService', function ($http, $window, ngToast) {
content: theTitle});
};
+ this.displayTimer = function (theTitle, timeMillis) {
+ ngToast.create({
+ className: "success",
+ content: theTitle + " in " + timeMillis + " milliseconds"});
+ };
+
this.viewDevices = function () {
return $http.get(this.state.base).then(
function (response) {
@@ -177,6 +187,28 @@ app.service ('bridgeService', function ($http, $window, ngToast) {
);
};
+ this.getSecurityInfo = function () {
+ return $http.get(this.state.systemsbase + "/securityinfo").then(
+ function (response) {
+ self.state.securityInfo = response.data;
+ },
+ function (error) {
+ self.displayWarn("Cannot get security info: ", error);
+ }
+ );
+ };
+
+ this.pushLinkButton = function () {
+ return $http.put(this.state.systemsbase + "/presslinkbutton").then(
+ function (response) {
+ self.displayTimer("Linnk your device in ", 30000);
+ },
+ function (error) {
+ self.displayWarn("Cannot get security info: ", error);
+ }
+ );
+ };
+
this.aContainsB = function (a, b) {
return a.indexOf(b) >= 0;
}
@@ -1228,6 +1260,9 @@ app.controller('ViewingController', function ($scope, $location, $http, $window,
$scope.renumberDevices = function() {
bridgeService.renumberDevices();
};
+ $scope.pushLinkButton = function() {
+ bridgeService.pushLinkButton();
+ };
$scope.backupDeviceDb = function (optionalbackupname) {
bridgeService.backupDeviceDb(optionalbackupname);
};
@@ -2934,9 +2969,6 @@ app.filter('configuredSomfyDevices', function (bridgeService) {
}
});
-
-
-
app.controller('VersionController', function ($scope, bridgeService) {
$scope.bridge = bridgeService.state;
});
diff --git a/src/main/resources/public/views/configuration.html b/src/main/resources/public/views/configuration.html
index 3cb3123..def1839 100644
--- a/src/main/resources/public/views/configuration.html
+++ b/src/main/resources/public/views/configuration.html
@@ -33,6 +33,7 @@
+
diff --git a/src/main/resources/public/views/system.html b/src/main/resources/public/views/system.html
index 7902872..d00cfbc 100644
--- a/src/main/resources/public/views/system.html
+++ b/src/main/resources/public/views/system.html
@@ -58,6 +58,8 @@
location.reload();
}
+
-
\ No newline at end of file
+
+
+