Continue adding security

This commit is contained in:
Admin
2017-03-27 16:50:29 -05:00
parent 6dfd70dfee
commit 895a9ec99b
14 changed files with 270 additions and 238 deletions

View File

@@ -5,7 +5,7 @@
<groupId>com.bwssystems.HABridge</groupId>
<artifactId>ha-bridge</artifactId>
<version>4.3.1Secure-d</version>
<version>4.3.1Secure-e</version>
<packaging>jar</packaging>
<name>HA Bridge</name>

View File

@@ -4,6 +4,7 @@ import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.GeneralSecurityException;
import java.util.Base64;
import java.util.HashMap;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
@@ -63,12 +64,39 @@ public class BridgeSecurity {
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);
if(securityDescriptor.getUsers() != null) {
User theUser = securityDescriptor.getUsers().get(aUser.getUsername());
if(theUser != null) {
theUser.setPassword(aUser.getPassword());
theUser.setPassword2(null);
settingsChanged = true;
}
else
error = "User not found";
}
else
error = "User not found";
}
}
else
error = "invalid user object given";
return error;
}
public String addUser(User aUser) throws IOException {
String error = null;
if(aUser != null) {
error = aUser.validate();
if(error == null) {
if(securityDescriptor.getUsers() == null)
securityDescriptor.setUsers(new HashMap<String, User>());
if(securityDescriptor.getUsers().get(aUser.getUsername()) == null) {
securityDescriptor.getUsers().put(aUser.getUsername(), aUser);
settingsChanged = true;
}
else
error = "Invalid request";
}
}
else
@@ -101,6 +129,7 @@ public class BridgeSecurity {
SecurityInfo theInfo = new SecurityInfo();
theInfo.setExecGarden(getExecGarden());
theInfo.setUseLinkButton(isUseLinkButton());
theInfo.setSecureHueApi(isSecureHueApi());
theInfo.setSecure(isSecure());
return theInfo;
}

View File

@@ -3,7 +3,7 @@ package com.bwssystems.HABridge;
public class SecurityInfo {
private boolean useLinkButton;
private String execGarden;
private boolean seucreHueApi;
private boolean secureHueApi;
private boolean isSecure;
public boolean isUseLinkButton() {
@@ -18,11 +18,11 @@ public class SecurityInfo {
public void setExecGarden(String execGarden) {
this.execGarden = execGarden;
}
public boolean isSeucreHueApi() {
return seucreHueApi;
public boolean isSecureHueApi() {
return secureHueApi;
}
public void setSeucreHueApi(boolean seucreHueApi) {
this.seucreHueApi = seucreHueApi;
public void setSecureHueApi(boolean secureHueApi) {
this.secureHueApi = secureHueApi;
}
public boolean isSecure() {
return isSecure;

View File

@@ -13,6 +13,7 @@ import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Timer;
import java.util.Base64;
import org.apache.http.HttpStatus;
import org.slf4j.Logger;
@@ -110,12 +111,70 @@ public class SystemControl extends AuthFramework {
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/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....");
String theDecodedPayload = new String(Base64.getDecoder().decode(request.body()));
User theUser = new Gson().fromJson(theDecodedPayload, User.class);
String errorMessage = bridgeSettings.getBridgeSecurity().setPassword(theUser);
if(errorMessage != null) {
response.status(HttpStatus.SC_BAD_REQUEST);
errorMessage = "{\"message\":\"" + errorMessage + "\"}";
}
else
response.status(HttpStatus.SC_OK);
return errorMessage;
});
// http://ip_address:port/system/adduser CORS request
options(SYSTEM_CONTEXT + "/adduser", "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/adduser which adds a new user
post(SYSTEM_CONTEXT + "/adduser", "application/json", (request, response) -> {
log.debug("adduser....");
String theDecodedPayload = new String(Base64.getDecoder().decode(request.body()));
User theUser = new Gson().fromJson(theDecodedPayload, User.class);
String errorMessage = theUser.validate();
if(errorMessage != null) {
response.status(HttpStatus.SC_BAD_REQUEST);
errorMessage = "{\"message\":\"" + errorMessage + "\"}";
} else {
response.status(HttpStatus.SC_OK);
}
return errorMessage;
});
// 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/presslinkbutton CORS request
options(SYSTEM_CONTEXT + "/presslinkbutton", "application/json", (request, response) -> {
@@ -135,20 +194,12 @@ public class SystemControl extends AuthFramework {
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/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/changesecurityinfo CORS request
options(SYSTEM_CONTEXT + "/changesecurityinfo", "application/json", (request, response) -> {
@@ -166,25 +217,10 @@ public class SystemControl extends AuthFramework {
if(theInfo.getExecGarden() != null)
bridgeSettings.getBridgeSecurity().setExecGarden(theInfo.getExecGarden());
bridgeSettings.getBridgeSecurity().setUseLinkButton(theInfo.isUseLinkButton());
bridgeSettings.getBridgeSecurity().setSecureHueApi(theInfo.isSeucreHueApi());
bridgeSettings.getBridgeSecurity().setSecureHueApi(theInfo.isSecureHueApi());
return bridgeSettings.getBridgeSecurity().getSecurityInfo();
}, 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);

View File

@@ -61,69 +61,6 @@ legend.form-label {
right: 10px;
}
.strength-meter {
position: relative;
height: 3px;
background: #DDD;
margin: 10px auto 20px;
border-radius: 3px;
}
.strength-meter:before,
.strength-meter:after {
content: '';
height: inherit;
background: transparent;
display: block;
border-color: #FFF;
border-style: solid;
border-width: 0 5px 0 5px;
position: absolute;
width: 80px;
z-index: 10;
}
.strength-meter:before {
left: 70px;
}
.strength-meter:after {
right: 70px;
}
.strength-meter-fill {
background: transparent;
height: inherit;
position: absolute;
width: 0;
border-radius: inherit;
transition: width 0.5s ease-in-out, background 0.25s;
}
.strength-meter-fill[data-strength='0'] {
background: darkred;
width: 20%;
}
.strength-meter-fill[data-strength='1'] {
background: orangered;
width: 40%;
}
.strength-meter-fill[data-strength='2'] {
background: orange;
width: 60%;
}
.strength-meter-fill[data-strength='3'] {
background: yellowgreen;
width: 80%;
}
.strength-meter-fill[data-strength='4'] {
background: green;
width: 100%;
}
.msg-block {
margin-top:5px;
}

View File

@@ -0,0 +1,11 @@
/*!
* @copyright Copyright &copy; Kartik Visweswaran, Krajee.com, 2015
* @package yii2-password
* @version 1.1.3
*
* Password Strength Meter
* Modified and built for Yii Framework 2.0
* Author: Kartik Visweswaran
* Year: 2015
* For more Yii related demos visit http://demos.krajee.com
*/.kv-strength-container{width:100%;margin:0;padding:0;border:0}.kv-strength-container td{vertical-align:middle}.kv-meter-container{width:130px}.kv-meter{text-align:center}.kv-disabled{opacity:.65;cursor:not-allowed}.kv-scorebar-border{background:none repeat scroll 0 0 #333;border:1px solid #333;height:16px;width:100px;margin:0 auto;border-radius:4px}.kv-scorebar{background-image:url(../img/bg_strength_gradient.jpg);background-repeat:no-repeat;background-position:0 0;position:absolute;width:98px;height:14px;z-index:0;border-radius:2px}.kv-score{font-weight:700;font-size:75%;position:absolute;width:98px;z-index:10;border-radius:2px}.kv-score-0,.kv-score-1,.kv-score-5{color:#fff}.kv-score-2,.kv-score-3,.kv-score-4{color:#333}.kv-verdict{width:100%}

Binary file not shown.

After

Width:  |  Height:  |  Size: 676 B

View File

@@ -13,6 +13,7 @@
<link href="css/ngDialog.min.css" rel="stylesheet">
<link href="css/ngDialog-theme-default.min.css" rel="stylesheet">
<link href="css/scrollable-table.css" rel="stylesheet">
<link href="css/strength-meter.min.css" rel="stylesheet">
<!--[if lt IE 9]>
<script type="text/javascript" src="js/html5shiv.min.js"></script>
@@ -81,7 +82,8 @@
<script src="js/rzslider.min.js"></script>
<script src="js/ngDialog.min.js"></script>
<script src="js/angular-scrollable-table.min.js"></script>
<script src="js/zxcvbn.js"></script>
<script src="js/strength-meter.min.js"></script>
<script src="js/angular-base64.min.js"></script>
<script src="scripts/app.js"></script>
</body>
</html>

View File

@@ -0,0 +1 @@
!function(){"use strict";angular.module("base64",[]).constant("$base64",function(){function a(a,b){var c=f.indexOf(a.charAt(b));if(-1==c)throw"Cannot decode base64";return c}function b(b){b=""+b;var c,d,f,g=b.length;if(0==g)return b;if(0!=g%4)throw"Cannot decode base64";c=0,b.charAt(g-1)==e&&(c=1,b.charAt(g-2)==e&&(c=2),g-=4);var h=[];for(d=0;g>d;d+=4)f=a(b,d)<<18|a(b,d+1)<<12|a(b,d+2)<<6|a(b,d+3),h.push(String.fromCharCode(f>>16,255&f>>8,255&f));switch(c){case 1:f=a(b,d)<<18|a(b,d+1)<<12|a(b,d+2)<<6,h.push(String.fromCharCode(f>>16,255&f>>8));break;case 2:f=a(b,d)<<18|a(b,d+1)<<12,h.push(String.fromCharCode(f>>16))}return h.join("")}function c(a,b){var c=a.charCodeAt(b);if(c>255)throw"INVALID_CHARACTER_ERR: DOM Exception 5";return c}function d(a){if(1!=arguments.length)throw"SyntaxError: Not enough arguments";var b,d,g=[];a=""+a;var h=a.length-a.length%3;if(0==a.length)return a;for(b=0;h>b;b+=3)d=c(a,b)<<16|c(a,b+1)<<8|c(a,b+2),g.push(f.charAt(d>>18)),g.push(f.charAt(63&d>>12)),g.push(f.charAt(63&d>>6)),g.push(f.charAt(63&d));switch(a.length-h){case 1:d=c(a,b)<<16,g.push(f.charAt(d>>18)+f.charAt(63&d>>12)+e+e);break;case 2:d=c(a,b)<<16|c(a,b+1)<<8,g.push(f.charAt(d>>18)+f.charAt(63&d>>12)+f.charAt(63&d>>6)+e)}return g.join("")}var e="=",f="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";return{encode:d,decode:b}}())}();

View File

@@ -1 +0,0 @@
!function(){"use strict";angular.module("zxcvbn",[]).directive("zxcvbn",function(){return{scope:{password:"=",extras:"=?",data:"=?"},restrict:"E",template:"{{ display.crack_times_display }}",link:function(r,a,t){r.$watch("password",function(a){angular.isString(a)&&(r.extras?r.timeToCrack=zxcvbn(a,r.extras):r.timeToCrack=zxcvbn(a),"data"in t&&r.timeToCrack&&(r.data=angular.copy(r.timeToCrack)),r.display=angular.copy(r.timeToCrack))})}}})}(),function(){"use strict";angular.module("zxcvbn").directive("zxcvbn",function(){return{require:"ngModel",restrict:"A",scope:{zxResult:"=?zxcvbn",zxExtras:"=?",zxMinScore:"@?"},link:function(r,a,t,e){r.runZxcvbn=function(){angular.isUndefined(r.zxPassword)&&(r.zxPassword=""),angular.isDefined(r.zxExtrasArray)&&r.zxExtrasArray.length>0?r.zxResult=zxcvbn(r.zxPassword,r.zxExtrasArray):r.zxResult=zxcvbn(r.zxPassword)},r.isForm=function(r){try{return"FormController"===Object.getPrototypeOf(r).constructor.name}catch(a){return!1}},r.setZxExtrasWatcher=function(){var a=r.zxExtras;angular.isFunction(r.zxExtrasWatcher)&&r.zxExtrasWatcher(),r.zxExtrasWatcher=void 0,angular.isDefined(a)&&(angular.isArray(a)?r.zxArrayWatcher():r.isForm(a)&&r.zxFormWatcher())},r.zxFormWatcher=function(){var a=r.zxExtras;console.assert(r.isForm(a),"zx-extras element is some how not a form."),r.zxExtrasWatcher=r.$watch(function(){var r=[],t=new RegExp("^(?!\\$|__)");for(var e in a)t.test(e)&&a[e].hasOwnProperty("$viewValue")&&-1===e.toLowerCase().indexOf("password")&&r.push(a[e].$viewValue);return r},function(a){r.zxExtrasArray=[];for(var t=0;t<a.length;t++)angular.isString(a[t])&&r.zxExtrasArray.push(a[t]);e.$validate()},!0)},r.zxArrayWatcher=function(){r.zxExtrasWatcher=r.$watch("zxExtras",function(a){r.zxExtrasArray=a,e.$validate()},!0)},r.setZxExtrasWatcher(),e.$validators.passwordStrength=function(a){var t=parseInt(r.zxMinScore);return t=isNaN(t)||0>t||t>4?0:t,r.zxPassword=a,r.runZxcvbn(),t<=r.zxResult.score},t.$observe("zxExtras",function(){r.setZxExtrasWatcher(),e.$validate()}),t.$observe("zxMinScore",function(a){r.zxMinScore=a,e.$validate()})}}})}();

File diff suppressed because one or more lines are too long

View File

@@ -1,4 +1,4 @@
var app = angular.module ('habridge', ['ngRoute', 'ngToast', 'rzModule', 'ngDialog', 'zxcvbn', 'scrollable-table']);
var app = angular.module ('habridge', ['ngRoute', 'ngToast', 'rzModule', 'ngDialog', 'base64', 'scrollable-table']);
app.config (function ($locationProvider, $routeProvider) {
$locationProvider.hashPrefix('!');
@@ -79,12 +79,12 @@ String.prototype.replaceAll = function (search, replace)
};
app.service ('bridgeService', function ($http, $window, ngToast, zxcvbn) {
app.service ('bridgeService', function ($http, $base64, 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: "", securityInfo: {}};
showDomoticz: false, showSomfy: false, showLifx: false, habridgeversion: "", viewDevId: "", queueDevId: "", securityInfo: {}, username: "test"};
this.displayWarn = function(errorTitle, error) {
var toastContent = errorTitle;
@@ -198,7 +198,7 @@ app.service ('bridgeService', function ($http, $window, ngToast, zxcvbn) {
);
};
this.changeSecuritySettings = function (useLinkButton, secureHueApi, execGarden, aPassword, aPassword2) {
this.changeSecuritySettings = function (useLinkButton, secureHueApi, execGarden) {
var newSecurityInfo = {};
newSecurityInfo = {
useLinkButton: useLinkButton,
@@ -216,6 +216,42 @@ app.service ('bridgeService', function ($http, $window, ngToast, zxcvbn) {
);
};
this.changePassword = function (aPassword, aPassword2) {
var newUserInfo = {};
newUserInfo = {
username: self.state.username,
password: aPassword,
password2: aPassword2
};
var theEncodedPayload = $base64.encode(angular.toJson(newUserInfo));
return $http.post(this.state.systemsbase + "/setpassword", theEncodedPayload ).then(
function (response) {
self.displaySuccess("Password updated")
},
function (error) {
self.displayWarn("Update password Error: ", error);
}
);
};
this.addUser = function (username, aPassword, aPassword2) {
var newUserInfo = {};
newUserInfo = {
username: username,
password: aPassword,
password2: aPassword2
};
var theEncodedPayload = $base64.encode(angular.toJson(newUserInfo));
return $http.post(this.state.systemsbase + "/adduser", theEncodedPayload ).then(
function (response) {
self.displaySuccess("User added")
},
function (error) {
self.displayWarn("User add Error: ", error);
}
);
};
this.pushLinkButton = function () {
return $http.put(this.state.systemsbase + "/presslinkbutton").then(
function (response) {
@@ -227,11 +263,6 @@ app.service ('bridgeService', function ($http, $window, ngToast, zxcvbn) {
);
};
this.score = function() {
var compute = zxcvbn.apply(null, arguments);
return compute && compute.score;
}
this.aContainsB = function (a, b) {
return a.indexOf(b) >= 0;
}
@@ -1004,7 +1035,7 @@ app.service ('bridgeService', function ($http, $window, ngToast, zxcvbn) {
};
});
app.controller ('SystemController', function ($scope, $location, $http, $window, bridgeService, ngDialog) {
app.controller ('SystemController', function ($scope, $location, bridgeService, ngDialog) {
bridgeService.viewConfigs();
bridgeService.loadBridgeSettings();
$scope.bridge = bridgeService.state;
@@ -1179,26 +1210,58 @@ app.controller ('SystemController', function ($scope, $location, $http, $window,
};
$scope.changeSeuritySettings = function () {
bridgeService.getSecurityInfo();
ngDialog.open({
template: 'securityDialog',
template: 'views/securityDialog.html',
controller: 'SecurityDialogCtrl',
className: 'ngdialog-theme-default'
});
};
});
app.directive('pwCheck', [function () {
return {
require: 'ngModel',
link: function (scope, elem, attrs, ctrl) {
var firstPassword = '#' + attrs.pwCheck;
elem.add(firstPassword).on('keyup', function () {
scope.$apply(function () {
// console.info(elem.val() === $(firstPassword).val());
ctrl.$setValidity('pwmatch', elem.val() === $(firstPassword).val());
scope.matched = (elem.val() === $(firstPassword).val());
});
});
}
}
}]);
app.controller('SecurityDialogCtrl', function ($scope, bridgeService, ngDialog) {
$scope.useLinkButton = bridgeService.state.securityInfo.useLinkButton;
$scope.username = bridgeService.state.username;
$scope.secureHueApi = bridgeService.state.securityInfo.secureHueApi;
$scope.useLinkButton = bridgeService.state.securityInfo.useLinkButton;
$scope.execGarden = bridgeService.state.securityInfo.execGarden;
$scope.matched = false;
$scope.setSecurityInfo = function () {
bridgeService.changeSecuritySettings($scope.useLinkButton, $scope.secureHueApi, $scope.execGarden);
};
$scope.changePassword = function () {
bridgeService.changePassword($scope.password, $scope.password2);
};
$scope.dismissDialog = function () {
ngDialog.close('ngdialog1');
bridgeService.changeSecuritySettings($scope.useLinkButton, $scope.secureHueApi, $scope.execGarden, $scope.password, $scope.password2);
};
$scope.setBlankPassword = function (theElementName) {
$scope.password = "";
var theElement = "#" + theElementName;
$(theElement).strength();
};
});
app.controller('LogsController', function ($scope, $location, $http, $window, bridgeService) {
app.controller('LogsController', function ($scope, $location, bridgeService) {
bridgeService.viewLogs();
$scope.bridge = bridgeService.state;
$scope.levels = ["INFO_INT", "WARN_INT", "DEBUG_INT", "TRACE_INT"];
@@ -1260,7 +1323,8 @@ function postrenderAction($timeout) {
}, 0);
}
}
app.controller('ViewingController', function ($scope, $location, $http, $window, bridgeService, ngDialog) {
app.controller('ViewingController', function ($scope, $location, bridgeService, ngDialog) {
bridgeService.viewDevices();
bridgeService.viewBackups();
@@ -1385,7 +1449,7 @@ app.controller('DeleteDialogCtrl', function ($scope, bridgeService, ngDialog) {
};
});
app.controller('VeraController', function ($scope, $location, $http, bridgeService, ngDialog) {
app.controller('VeraController', function ($scope, $location, bridgeService, ngDialog) {
$scope.bridge = bridgeService.state;
$scope.device = bridgeService.state.device;
$scope.device_dim_control = "";
@@ -1540,7 +1604,7 @@ app.controller('VeraController', function ($scope, $location, $http, bridgeServi
};
});
app.controller('HarmonyController', function ($scope, $location, $http, bridgeService, ngDialog) {
app.controller('HarmonyController', function ($scope, $location, bridgeService, ngDialog) {
$scope.bridge = bridgeService.state;
$scope.device = bridgeService.state.device;
bridgeService.viewHarmonyActivities();
@@ -1601,7 +1665,7 @@ app.controller('HarmonyController', function ($scope, $location, $http, bridgeSe
};
});
app.controller('NestController', function ($scope, $location, $http, bridgeService, ngDialog) {
app.controller('NestController', function ($scope, $location, bridgeService, ngDialog) {
$scope.bridge = bridgeService.state;
$scope.device = bridgeService.state.device;
bridgeService.viewNestItems();
@@ -1702,7 +1766,7 @@ app.controller('NestController', function ($scope, $location, $http, bridgeServi
};
});
app.controller('HueController', function ($scope, $location, $http, bridgeService, ngDialog) {
app.controller('HueController', function ($scope, $location, bridgeService, ngDialog) {
$scope.bridge = bridgeService.state;
$scope.device = bridgeService.state.device;
$scope.bulk = { devices: [] };
@@ -1823,7 +1887,7 @@ app.controller('HueController', function ($scope, $location, $http, bridgeServic
};
});
app.controller('HalController', function ($scope, $location, $http, bridgeService, ngDialog) {
app.controller('HalController', function ($scope, $location, bridgeService, ngDialog) {
$scope.bridge = bridgeService.state;
$scope.device = bridgeService.state.device;
$scope.device_dim_control = "";
@@ -2128,7 +2192,7 @@ app.controller('HalController', function ($scope, $location, $http, bridgeServic
};
});
app.controller('MQTTController', function ($scope, $location, $http, bridgeService, ngDialog) {
app.controller('MQTTController', function ($scope, $location, bridgeService, ngDialog) {
$scope.bridge = bridgeService.state;
$scope.device = bridgeService.state.device;
bridgeService.viewMQTTDevices();
@@ -2173,7 +2237,7 @@ app.controller('MQTTController', function ($scope, $location, $http, bridgeServi
};
});
app.controller('HassController', function ($scope, $location, $http, bridgeService, ngDialog) {
app.controller('HassController', function ($scope, $location, bridgeService, ngDialog) {
$scope.bridge = bridgeService.state;
$scope.device = bridgeService.state.device;
$scope.device_dim_control = "";
@@ -2299,7 +2363,7 @@ app.controller('HassController', function ($scope, $location, $http, bridgeServi
};
});
app.controller('DomoticzController', function ($scope, $location, $http, bridgeService, ngDialog) {
app.controller('DomoticzController', function ($scope, $location, bridgeService, ngDialog) {
$scope.bridge = bridgeService.state;
$scope.device = bridgeService.state.device;
$scope.device_dim_control = "";
@@ -2454,7 +2518,7 @@ app.controller('DomoticzController', function ($scope, $location, $http, bridgeS
};
});
app.controller('LifxController', function ($scope, $location, $http, bridgeService, ngDialog) {
app.controller('LifxController', function ($scope, $location, bridgeService, ngDialog) {
$scope.bridge = bridgeService.state;
$scope.device = bridgeService.state.device;
$scope.device_dim_control = "";
@@ -2576,7 +2640,7 @@ app.controller('LifxController', function ($scope, $location, $http, bridgeServi
};
});
app.controller('SomfyController', function ($scope, $location, $http, bridgeService, ngDialog) {
app.controller('SomfyController', function ($scope, $location, bridgeService, ngDialog) {
$scope.bridge = bridgeService.state;
$scope.device = bridgeService.state.device;
$scope.device_dim_control = "";
@@ -2704,7 +2768,7 @@ app.controller('SomfyController', function ($scope, $location, $http, bridgeServ
};
});
app.controller('EditController', function ($scope, $location, $http, bridgeService) {
app.controller('EditController', function ($scope, $location, bridgeService) {
bridgeService.viewMapTypes();
$scope.bridge = bridgeService.state;
$scope.device = bridgeService.state.device;
@@ -3011,58 +3075,6 @@ app.filter('configuredSomfyDevices', function (bridgeService) {
}
});
app.filter('passwordCount', [function() {
return function(value, peak) {
value = angular.isString(value) ? value : '';
peak = isFinite(peak) ? peak : 7;
return value && (value.length > peak ? peak + '+' : value.length);
};
}]);
app.directive('okPassword', ['bridgeService', function(bridgeService) {
return {
// restrict to only attribute and class
restrict: 'AC',
// use the NgModelController
require: 'ngModel',
// add the NgModelController as a dependency to your link function
link: function($scope, $element, $attrs, ngModelCtrl) {
$element.on('blur change keydown', function(evt) {
$scope.$evalAsync(function($scope) {
// update the $scope.password with the element's value
var pwd = $scope.password = $element.val();
// resolve password strength score using zxcvbn service
$scope.passwordStrength = pwd ? (pwd.length > 7 && bridgeService.score(pwd) || 0) : null;
// define the validity criterion for okPassword constraint
ngModelCtrl.$setValidity('okPassword', $scope.passwordStrength >= 2);
});
});
}
};
}]);
app.controller('FormController', function($scope) {});
app.directive('pwCheck', [function () {
return {
require: 'ngModel',
link: function (scope, elem, attrs, ctrl) {
var firstPassword = '#' + attrs.pwCheck;
elem.add(firstPassword).on('keyup', function () {
scope.$apply(function () {
// console.info(elem.val() === $(firstPassword).val());
ctrl.$setValidity('pwmatch', elem.val() === $(firstPassword).val());
});
});
}
}
}]);
app.controller('VersionController', function ($scope, bridgeService) {
$scope.bridge = bridgeService.state;
});

View File

@@ -0,0 +1,45 @@
<div class="form-container ngdialog-message" ng-controller="SecurityDialogCtrl" postrender-action="setBlankPassword('password-1')">
<form name="securityForm" role="form">
<legend class="form-label">Update Security Settings</legend>
<div class="form-group">
<label>Use Link Button</label>
<input type="checkbox"
ng-model="useLinkButton" ng-true-value=true
ng-false-value=false> {{useLinkButton}}
</div>
<div class="form-group">
<label>Use username/password for HUE Api</label>
<input type="checkbox"
ng-model="secureHueApi" ng-true-value=true
ng-false-value=false> {{secureHueApi}}
</div>
<div class="form-group">
<label>Secure Folder for scripts/executables</label>
<input id="exec-garden" class="form-control"
type="text" ng-model="execGarden"
placeholder="/home/pi/protectedscripts">
</div>
<div class="form-group">
<button type="button" class="btn btn-primary" ng-click="setSecurityInfo()">Update</button>
</div>
<div class="form-group">
<label>Change Password for {{username}}</label>
<input id="password-1" name="password-1" type="password" class="form-control strength" ng-model="password" data-toggle-title="Display Password" />
</div>
<div class="form-group">
<label>Confirm Password</label>
<input id="password-2" name="password-2" class="form-control" type="password" ng-model="password2" pw-check="password-1" />
<div class="msg-block" ng-show="securityForm.$error">
<span class="msg-error" ng-show="securityForm.$error.pwmatch">Passwords don't match.</span>
</div>
</div>
<div ng-if="matched" class="form-group">
<button class="btn btn-warning" ng-click="changePassword()">Change Password</button>
</div>
<div class="form-group">
<button type="button" class="btn btn-success" ng-click="dismissDialog()">Dismiss</button>
</div>
</form>
</div>

View File

@@ -512,52 +512,4 @@
</tr>
</table>
</div>
</div>
<script type="text/ng-template" id="securityDialog">
<div class="form-container">
<form name="securityForm" role="form" ng-controller="FormController">
<legend class="form-label">Update Security Settings</legend>
<div class="form-group">
<label>Use Link Button </label>
<input type="checkbox"
ng-model="useLinkButton" ng-true-value=true
ng-false-value=false> {{useLinkButton}}
</div>
<div class="form-group">
<label>Use username/password for HUE Api </label>
<input type="checkbox"
ng-model="secureHueApi" ng-true-value=true
ng-false-value=false> {{secureHueApi}}
</div>
<div class="form-group">
<label>Secure Folder for scripts/executables</label>
<input id="exec-garden" class="form-control"
type="text" ng-model="execGarden"
placeholder="/home/pi/protectedscripts">
</div>
<div class="form-group">
<label>Change Password</label>
<div class="form-hint">If you are exposing this to the internet, you use a sufficiently strong password. Password must be more than 7 characters.</div>
<input type="password" class="form-control ok-password" ng-class="(securityForm.password.$dirty && securityForm.password.$invalid) ? 'error' : ''" id="password" name="password" placeholder="Enter Password" ng-required="true" ng-model="password">
<div class="label password-count" ng-class="password.length > 7 ? 'label-success' : 'label-danger'" ng-cloak>{{ password | passwordCount:7 }}</div>
<div class="strength-meter">
<div class="strength-meter-fill" data-strength="{{passwordStrength}}"></div>
</div>
</div>
<div class="form-group">
<label>Confirm Password</label>
<input id="password-2" class="form-control" type="password" ng-model="password2" ng-required="true" pw-check="password" />
<div class="msg-block" ng-show="securityForm.$error">
<span class="msg-error" ng-show="securityForm.pw2.$error.pwmatch">Passwords don't match.</span>
</div>
</div>
<button type="button" class="ngdialog-button ngdialog-button-primary" ng-disabled="securityForm.$invalid" ng-click="setSecurityInfo()">Update</button>
</form>
</div>
</script>
</div>