mirror of
https://github.com/bwssytems/ha-bridge.git
synced 2025-12-16 18:24:36 +00:00
Continue with security update
This commit is contained in:
4
pom.xml
4
pom.xml
@@ -5,7 +5,7 @@
|
||||
|
||||
<groupId>com.bwssystems.HABridge</groupId>
|
||||
<artifactId>ha-bridge</artifactId>
|
||||
<version>4.3.1Secure-b</version>
|
||||
<version>4.3.1Secure-c</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>HA Bridge</name>
|
||||
@@ -63,7 +63,7 @@
|
||||
<dependency>
|
||||
<groupId>com.sparkjava</groupId>
|
||||
<artifactId>spark-core</artifactId>
|
||||
<version>2.3</version>
|
||||
<version>2.5.5</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<artifactId>slf4j-simple</artifactId>
|
||||
|
||||
25
src/main/java/com/bwssystems/HABridge/AuthFramework.java
Normal file
25
src/main/java/com/bwssystems/HABridge/AuthFramework.java
Normal file
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -1,33 +1,24 @@
|
||||
package com.bwssystems.HABridge;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class BridgeSecurityDescriptor {
|
||||
private String uiPassword;
|
||||
private boolean passwordSet;
|
||||
private Map<String, User> 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<String, User> 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<String, User> 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<String, User> entry : users.entrySet())
|
||||
{
|
||||
if(entry.getValue().getPassword() != null && !entry.getValue().getPassword().isEmpty()) {
|
||||
secureFlag = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return secureFlag;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
33
src/main/java/com/bwssystems/HABridge/SecurityInfo.java
Normal file
33
src/main/java/com/bwssystems/HABridge/SecurityInfo.java
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
65
src/main/java/com/bwssystems/HABridge/User.java
Normal file
65
src/main/java/com/bwssystems/HABridge/User.java
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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";
|
||||
|
||||
|
||||
@@ -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;
|
||||
});
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
|
||||
<p>
|
||||
<button class="btn btn-primary" type="submit" ng-click="renumberDevices()">Renumber Devices</button>
|
||||
<button ng-if="bridge.securityInfo.useLinkButton" class="btn btn-primary" type="submit" ng-click="pushLinkButton()">Link</button>
|
||||
</p>
|
||||
<scrollable-table watch="bridge.devices">
|
||||
<table class="table table-bordered table-striped table-hover">
|
||||
|
||||
@@ -58,6 +58,8 @@
|
||||
location.reload();
|
||||
}
|
||||
</script>
|
||||
<button class="btn btn-warning"
|
||||
type="submit" ng-click="updateSecurity()">Update Security Settings</button>
|
||||
</p>
|
||||
<table class="table table-bordered table-striped table-hover">
|
||||
<thead>
|
||||
@@ -510,4 +512,20 @@
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/ng-template" id="securityDialog">
|
||||
<div class="ngdialog-message">
|
||||
<h2>Select value</h2>
|
||||
<p>
|
||||
<input type="radio" ng-model="valueType" value="percentage" ng-change="changeScale()"> Percentage
|
||||
<input type="radio" ng-model="valueType" value="raw" ng-change="changeScale()"> Raw
|
||||
</p>
|
||||
<p>
|
||||
<rzslider rz-slider-model="slider.value" rz-slider-options="slider.options"></rzslider>
|
||||
</p>
|
||||
</div>
|
||||
<div class="ngdialog-buttons mt">
|
||||
<button type="button" class="ngdialog-button ngdialog-button-primary" ng-click="setValue()">Set</button>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user