[unifi] Added support for UniFi OS (#10041)

Signed-off-by: Mathias Maes <watcherwhale@maes.family>
This commit is contained in:
Mathias 2021-02-04 20:34:38 +01:00 committed by GitHub
parent d452469450
commit 6b4fc99164
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 63 additions and 13 deletions

View File

@ -53,6 +53,7 @@ public class UniFiBindingConstants {
public static final String PARAMETER_PORT = "port"; public static final String PARAMETER_PORT = "port";
public static final String PARAMETER_USERNAME = "username"; public static final String PARAMETER_USERNAME = "username";
public static final String PARAMETER_PASSWORD = "password"; public static final String PARAMETER_PASSWORD = "password";
public static final String PARAMETER_UNIFIOS = "unifios";
public static final String PARAMETER_SITE = "site"; public static final String PARAMETER_SITE = "site";
public static final String PARAMETER_CID = "cid"; public static final String PARAMETER_CID = "cid";
} }

View File

@ -33,6 +33,8 @@ public class UniFiControllerThingConfig {
private int refresh = 10; private int refresh = 10;
private boolean unifios = false;
public String getHost() { public String getHost() {
return host; return host;
} }
@ -53,6 +55,10 @@ public class UniFiControllerThingConfig {
return refresh; return refresh;
} }
public boolean isUniFiOS() {
return unifios;
}
public boolean isValid() { public boolean isValid() {
return StringUtils.isNotBlank(host) && StringUtils.isNotBlank(username) && StringUtils.isNotBlank(password); return StringUtils.isNotBlank(host) && StringUtils.isNotBlank(username) && StringUtils.isNotBlank(password);
} }
@ -60,6 +66,6 @@ public class UniFiControllerThingConfig {
@Override @Override
public String toString() { public String toString() {
return "UniFiControllerConfig{host = " + host + ", port = " + port + ", username = " + username return "UniFiControllerConfig{host = " + host + ", port = " + port + ", username = " + username
+ ", password = *****, refresh = " + refresh + "}"; + ", password = *****, refresh = " + refresh + ", unifios = " + unifios + "}";
} }
} }

View File

@ -65,14 +65,21 @@ public class UniFiController {
private final String password; private final String password;
private final boolean unifios;
private String csrfToken;
private final Gson gson; private final Gson gson;
public UniFiController(HttpClient httpClient, String host, int port, String username, String password) { public UniFiController(HttpClient httpClient, String host, int port, String username, String password,
boolean unifios) {
this.httpClient = httpClient; this.httpClient = httpClient;
this.host = host; this.host = host;
this.port = port; this.port = port;
this.username = username; this.username = username;
this.password = password; this.password = password;
this.unifios = unifios;
this.csrfToken = "";
UniFiSiteInstanceCreator siteInstanceCreator = new UniFiSiteInstanceCreator(this); UniFiSiteInstanceCreator siteInstanceCreator = new UniFiSiteInstanceCreator(this);
UniFiDeviceInstanceCreator deviceInstanceCreator = new UniFiDeviceInstanceCreator(this); UniFiDeviceInstanceCreator deviceInstanceCreator = new UniFiDeviceInstanceCreator(this);
UniFiClientInstanceCreator clientInstanceCreator = new UniFiClientInstanceCreator(this); UniFiClientInstanceCreator clientInstanceCreator = new UniFiClientInstanceCreator(this);
@ -96,8 +103,10 @@ public class UniFiController {
} }
public void login() throws UniFiException { public void login() throws UniFiException {
csrfToken = "";
UniFiControllerRequest<Void> req = newRequest(Void.class); UniFiControllerRequest<Void> req = newRequest(Void.class);
req.setPath("/api/login"); req.setPath(unifios ? "/api/auth/login" : "/api/login");
req.setBodyParameter("username", username); req.setBodyParameter("username", username);
req.setBodyParameter("password", password); req.setBodyParameter("password", password);
// scurb: Changed strict = false to make blocking feature work // scurb: Changed strict = false to make blocking feature work
@ -107,8 +116,9 @@ public class UniFiController {
} }
public void logout() throws UniFiException { public void logout() throws UniFiException {
csrfToken = "";
UniFiControllerRequest<Void> req = newRequest(Void.class); UniFiControllerRequest<Void> req = newRequest(Void.class);
req.setPath("/logout"); req.setPath(unifios ? "/api/auth/logout" : "/logout");
executeRequest(req); executeRequest(req);
} }
@ -172,7 +182,7 @@ public class UniFiController {
protected void block(UniFiClient client, boolean blocked) throws UniFiException { protected void block(UniFiClient client, boolean blocked) throws UniFiException {
UniFiControllerRequest<Void> req = newRequest(Void.class); UniFiControllerRequest<Void> req = newRequest(Void.class);
req.setPath("/api/s/" + client.getSite().getName() + "/cmd/stamgr"); req.setAPIPath("/api/s/" + client.getSite().getName() + "/cmd/stamgr");
req.setBodyParameter("cmd", blocked ? "block-sta" : "unblock-sta"); req.setBodyParameter("cmd", blocked ? "block-sta" : "unblock-sta");
req.setBodyParameter("mac", client.getMac()); req.setBodyParameter("mac", client.getMac());
executeRequest(req); executeRequest(req);
@ -180,7 +190,7 @@ public class UniFiController {
protected void reconnect(UniFiClient client) throws UniFiException { protected void reconnect(UniFiClient client) throws UniFiException {
UniFiControllerRequest<Void> req = newRequest(Void.class); UniFiControllerRequest<Void> req = newRequest(Void.class);
req.setPath("/api/s/" + client.getSite().getName() + "/cmd/stamgr"); req.setAPIPath("/api/s/" + client.getSite().getName() + "/cmd/stamgr");
req.setBodyParameter("cmd", "kick-sta"); req.setBodyParameter("cmd", "kick-sta");
req.setBodyParameter("mac", client.getMac()); req.setBodyParameter("mac", client.getMac());
executeRequest(req); executeRequest(req);
@ -189,13 +199,14 @@ public class UniFiController {
// Internal API // Internal API
private <T> UniFiControllerRequest<T> newRequest(Class<T> responseType) { private <T> UniFiControllerRequest<T> newRequest(Class<T> responseType) {
return new UniFiControllerRequest<>(responseType, gson, httpClient, host, port); return new UniFiControllerRequest<>(responseType, gson, httpClient, host, port, csrfToken, unifios);
} }
private <T> @Nullable T executeRequest(UniFiControllerRequest<T> request) throws UniFiException { private <T> @Nullable T executeRequest(UniFiControllerRequest<T> request) throws UniFiException {
T result; T result;
try { try {
result = request.execute(); result = request.execute();
csrfToken = request.getCsrfToken();
} catch (UniFiExpiredSessionException e) { } catch (UniFiExpiredSessionException e) {
login(); login();
result = executeRequest(request); result = executeRequest(request);
@ -208,7 +219,7 @@ public class UniFiController {
private UniFiSiteCache getSites() throws UniFiException { private UniFiSiteCache getSites() throws UniFiException {
UniFiControllerRequest<UniFiSite[]> req = newRequest(UniFiSite[].class); UniFiControllerRequest<UniFiSite[]> req = newRequest(UniFiSite[].class);
req.setPath("/api/self/sites"); req.setAPIPath("/api/self/sites");
UniFiSite[] sites = executeRequest(req); UniFiSite[] sites = executeRequest(req);
UniFiSiteCache cache = new UniFiSiteCache(); UniFiSiteCache cache = new UniFiSiteCache();
if (sites != null) { if (sites != null) {
@ -231,7 +242,7 @@ public class UniFiController {
private UniFiDeviceCache getDevices(UniFiSite site) throws UniFiException { private UniFiDeviceCache getDevices(UniFiSite site) throws UniFiException {
UniFiControllerRequest<UniFiDevice[]> req = newRequest(UniFiDevice[].class); UniFiControllerRequest<UniFiDevice[]> req = newRequest(UniFiDevice[].class);
req.setPath("/api/s/" + site.getName() + "/stat/device"); req.setAPIPath("/api/s/" + site.getName() + "/stat/device");
UniFiDevice[] devices = executeRequest(req); UniFiDevice[] devices = executeRequest(req);
UniFiDeviceCache cache = new UniFiDeviceCache(); UniFiDeviceCache cache = new UniFiDeviceCache();
if (devices != null) { if (devices != null) {
@ -254,7 +265,7 @@ public class UniFiController {
private UniFiClientCache getClients(UniFiSite site) throws UniFiException { private UniFiClientCache getClients(UniFiSite site) throws UniFiException {
UniFiControllerRequest<UniFiClient[]> req = newRequest(UniFiClient[].class); UniFiControllerRequest<UniFiClient[]> req = newRequest(UniFiClient[].class);
req.setPath("/api/s/" + site.getName() + "/stat/sta"); req.setAPIPath("/api/s/" + site.getName() + "/stat/sta");
UniFiClient[] clients = executeRequest(req); UniFiClient[] clients = executeRequest(req);
UniFiClientCache cache = new UniFiClientCache(); UniFiClientCache cache = new UniFiClientCache();
if (clients != null) { if (clients != null) {
@ -277,7 +288,7 @@ public class UniFiController {
private UniFiClientCache getInsights(UniFiSite site) throws UniFiException { private UniFiClientCache getInsights(UniFiSite site) throws UniFiException {
UniFiControllerRequest<UniFiClient[]> req = newRequest(UniFiClient[].class); UniFiControllerRequest<UniFiClient[]> req = newRequest(UniFiClient[].class);
req.setPath("/api/s/" + site.getName() + "/stat/alluser"); req.setAPIPath("/api/s/" + site.getName() + "/stat/alluser");
req.setQueryParameter("within", 168); // scurb: Changed to 7 days. req.setQueryParameter("within", 168); // scurb: Changed to 7 days.
UniFiClient[] clients = executeRequest(req); UniFiClient[] clients = executeRequest(req);
UniFiClientCache cache = new UniFiClientCache(); UniFiClientCache cache = new UniFiClientCache();

View File

@ -83,6 +83,10 @@ public class UniFiControllerRequest<T> {
private String path = "/"; private String path = "/";
private final boolean unifios;
private String csrfToken;
private Map<String, String> queryParameters = new HashMap<>(); private Map<String, String> queryParameters = new HashMap<>();
private Map<String, String> bodyParameters = new HashMap<>(); private Map<String, String> bodyParameters = new HashMap<>();
@ -91,12 +95,23 @@ public class UniFiControllerRequest<T> {
// Public API // Public API
public UniFiControllerRequest(Class<T> resultType, Gson gson, HttpClient httpClient, String host, int port) { public UniFiControllerRequest(Class<T> resultType, Gson gson, HttpClient httpClient, String host, int port,
String csrfToken, boolean unifios) {
this.resultType = resultType; this.resultType = resultType;
this.gson = gson; this.gson = gson;
this.httpClient = httpClient; this.httpClient = httpClient;
this.host = host; this.host = host;
this.port = port; this.port = port;
this.csrfToken = csrfToken;
this.unifios = unifios;
}
public void setAPIPath(String relativePath) {
if (unifios) {
this.path = "/proxy/network" + relativePath;
} else {
this.path = relativePath;
}
} }
public void setPath(String path) { public void setPath(String path) {
@ -136,6 +151,11 @@ public class UniFiControllerRequest<T> {
if (logger.isTraceEnabled()) { if (logger.isTraceEnabled()) {
logger.trace("<< {} {} \n{}", status, HttpStatus.getMessage(status), prettyPrintJson(content)); logger.trace("<< {} {} \n{}", status, HttpStatus.getMessage(status), prettyPrintJson(content));
} }
String csrfToken = response.getHeaders().get("X-CSRF-Token");
if (csrfToken != null && !csrfToken.isEmpty()) {
this.csrfToken = csrfToken;
}
break; break;
case HttpStatus.BAD_REQUEST_400: case HttpStatus.BAD_REQUEST_400:
throw new UniFiInvalidCredentialsException("Invalid Credentials"); throw new UniFiInvalidCredentialsException("Invalid Credentials");
@ -184,6 +204,10 @@ public class UniFiControllerRequest<T> {
return response; return response;
} }
public String getCsrfToken() {
return csrfToken;
}
private Request newRequest() { private Request newRequest() {
HttpMethod method = bodyParameters.isEmpty() ? HttpMethod.GET : HttpMethod.POST; HttpMethod method = bodyParameters.isEmpty() ? HttpMethod.GET : HttpMethod.POST;
HttpURI uri = new HttpURI(HttpScheme.HTTPS.asString(), host, port, path); HttpURI uri = new HttpURI(HttpScheme.HTTPS.asString(), host, port, path);
@ -198,6 +222,10 @@ public class UniFiControllerRequest<T> {
StandardCharsets.UTF_8); StandardCharsets.UTF_8);
request = request.content(content); request = request.content(content);
} }
if (!csrfToken.isEmpty())
request.header("x-csrf-token", this.csrfToken);
return request; return request;
} }

View File

@ -88,7 +88,7 @@ public class UniFiControllerThingHandler extends BaseBridgeHandler {
logger.debug("Initializing the UniFi Controller Handler with config = {}", config); logger.debug("Initializing the UniFi Controller Handler with config = {}", config);
try { try {
controller = new UniFiController(httpClient, config.getHost(), config.getPort(), config.getUsername(), controller = new UniFiController(httpClient, config.getHost(), config.getPort(), config.getUsername(),
config.getPassword()); config.getPassword(), config.isUniFiOS());
controller.start(); controller.start();
updateStatus(ONLINE); updateStatus(ONLINE);
} catch (UniFiInvalidHostException e) { } catch (UniFiInvalidHostException e) {

View File

@ -21,6 +21,10 @@
<description>Port of the UniFi Controller</description> <description>Port of the UniFi Controller</description>
<default>8443</default> <default>8443</default>
</parameter> </parameter>
<parameter name="unifios" type="boolean" required="true">
<label>UniFi OS</label>
<description>If the UniFi Controller is running on UniFi OS.</description>
</parameter>
<parameter name="username" type="text" required="true"> <parameter name="username" type="text" required="true">
<label>Username</label> <label>Username</label>
<description>The username to access the UniFi Controller.</description> <description>The username to access the UniFi Controller.</description>