|
|
|
|
@@ -8,6 +8,7 @@
|
|
|
|
|
|
|
|
|
|
package com.facebook.openwifirrm.ucentral;
|
|
|
|
|
|
|
|
|
|
import java.time.Instant;
|
|
|
|
|
import java.util.Collections;
|
|
|
|
|
import java.util.HashMap;
|
|
|
|
|
import java.util.List;
|
|
|
|
|
@@ -30,6 +31,8 @@ import com.facebook.openwifirrm.ucentral.gw.models.ServiceEvent;
|
|
|
|
|
import com.facebook.openwifirrm.ucentral.gw.models.StatisticsRecords;
|
|
|
|
|
import com.facebook.openwifirrm.ucentral.gw.models.SystemInfoResults;
|
|
|
|
|
import com.facebook.openwifirrm.ucentral.gw.models.TokenValidationResult;
|
|
|
|
|
import com.facebook.openwifirrm.ucentral.gw.models.WebTokenRefreshRequest;
|
|
|
|
|
import com.facebook.openwifirrm.ucentral.gw.models.WebTokenResult;
|
|
|
|
|
import com.facebook.openwifirrm.ucentral.gw.models.WifiScanRequest;
|
|
|
|
|
import com.facebook.openwifirrm.ucentral.prov.models.EntityList;
|
|
|
|
|
import com.facebook.openwifirrm.ucentral.prov.models.InventoryTagList;
|
|
|
|
|
@@ -137,7 +140,18 @@ public class UCentralClient {
|
|
|
|
|
* The access token obtained from uCentralSec, needed only when using public
|
|
|
|
|
* endpoints.
|
|
|
|
|
*/
|
|
|
|
|
private String accessToken;
|
|
|
|
|
private WebTokenResult accessToken;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* The unix timestamp (in seconds) keeps track of when the accessToken is created.
|
|
|
|
|
*/
|
|
|
|
|
private long created;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* The unix timestamp (in seconds) keeps track of last time when the accessToken
|
|
|
|
|
* is accessed.
|
|
|
|
|
*/
|
|
|
|
|
private long lastAccess;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Constructor.
|
|
|
|
|
@@ -184,7 +198,8 @@ public class UCentralClient {
|
|
|
|
|
Map<String, Object> body = new HashMap<>();
|
|
|
|
|
body.put("userId", username);
|
|
|
|
|
body.put("password", password);
|
|
|
|
|
HttpResponse<String> response = httpPost("oauth2", OWSEC_SERVICE, body);
|
|
|
|
|
HttpResponse<String> response =
|
|
|
|
|
httpPost("oauth2", OWSEC_SERVICE, body, null);
|
|
|
|
|
if (!response.isSuccess()) {
|
|
|
|
|
logger.error(
|
|
|
|
|
"Login failed: Response code {}, body: {}",
|
|
|
|
|
@@ -195,27 +210,146 @@ public class UCentralClient {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Parse access token from response
|
|
|
|
|
JSONObject respBody;
|
|
|
|
|
WebTokenResult token;
|
|
|
|
|
try {
|
|
|
|
|
respBody = new JSONObject(response.getBody());
|
|
|
|
|
} catch (JSONException e) {
|
|
|
|
|
token = gson.fromJson(response.getBody(), WebTokenResult.class);
|
|
|
|
|
} catch (JsonSyntaxException e) {
|
|
|
|
|
logger.error("Login failed: Unexpected response", e);
|
|
|
|
|
logger.debug("Response body: {}", response.getBody());
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (!respBody.has("access_token")) {
|
|
|
|
|
if (
|
|
|
|
|
token == null || token.access_token == null ||
|
|
|
|
|
token.access_token.isEmpty()
|
|
|
|
|
) {
|
|
|
|
|
logger.error("Login failed: Missing access token");
|
|
|
|
|
logger.debug("Response body: {}", respBody.toString());
|
|
|
|
|
logger.debug("Response body: {}", response.getBody());
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
this.accessToken = respBody.getString("access_token");
|
|
|
|
|
this.accessToken = token;
|
|
|
|
|
this.created = accessToken.created;
|
|
|
|
|
this.lastAccess = accessToken.created;
|
|
|
|
|
logger.info("Login successful as user: {}", username);
|
|
|
|
|
logger.debug("Access token: {}", accessToken);
|
|
|
|
|
logger.debug("Access token: {}", accessToken.access_token);
|
|
|
|
|
logger.debug("Refresh token: {}", accessToken.refresh_token);
|
|
|
|
|
|
|
|
|
|
// Load system endpoints
|
|
|
|
|
return loadSystemEndpoints();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* when using public endpoints, refresh the access token if it's expired.
|
|
|
|
|
*/
|
|
|
|
|
public synchronized void refreshAccessToken() {
|
|
|
|
|
if (usePublicEndpoints) {
|
|
|
|
|
refreshAccessTokenImpl();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Check if the token is completely expired even if
|
|
|
|
|
* for a token refresh request
|
|
|
|
|
*
|
|
|
|
|
* @return true if the refresh token is expired
|
|
|
|
|
*/
|
|
|
|
|
private boolean isAccessTokenExpired() {
|
|
|
|
|
if (accessToken == null) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return created + accessToken.expires_in <
|
|
|
|
|
Instant.now().getEpochSecond();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Check if an access token is expired.
|
|
|
|
|
*
|
|
|
|
|
* @return true if the access token is expired
|
|
|
|
|
*/
|
|
|
|
|
private boolean isAccessTokenTimedOut() {
|
|
|
|
|
if (accessToken == null) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return lastAccess + accessToken.idle_timeout <
|
|
|
|
|
Instant.now().getEpochSecond();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Refresh the access toke when time out. If the refresh token is expired, login again.
|
|
|
|
|
* If the access token is expired, POST a WebTokenRefreshRequest to refresh token.
|
|
|
|
|
*/
|
|
|
|
|
private void refreshAccessTokenImpl() {
|
|
|
|
|
if (!usePublicEndpoints) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (isAccessTokenExpired()) {
|
|
|
|
|
synchronized (this) {
|
|
|
|
|
if (isAccessTokenExpired()) {
|
|
|
|
|
logger.info("Token is expired, login again");
|
|
|
|
|
login();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else if (isAccessTokenTimedOut()) {
|
|
|
|
|
synchronized (this) {
|
|
|
|
|
if (isAccessTokenTimedOut()) {
|
|
|
|
|
logger.debug("Access token timed out, refreshing the token");
|
|
|
|
|
accessToken = refreshToken();
|
|
|
|
|
created = Instant.now().getEpochSecond();
|
|
|
|
|
lastAccess = created;
|
|
|
|
|
if (accessToken != null) {
|
|
|
|
|
logger.debug("Successfully refresh token.");
|
|
|
|
|
}else{
|
|
|
|
|
logger.error(
|
|
|
|
|
"Fail to refresh token with access token: {}",
|
|
|
|
|
accessToken.access_token
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* POST a WebTokenRefreshRequest to refresh the access token.
|
|
|
|
|
*
|
|
|
|
|
* @return valid access token if success, otherwise return null.
|
|
|
|
|
*/
|
|
|
|
|
private WebTokenResult refreshToken() {
|
|
|
|
|
if (accessToken == null) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
WebTokenRefreshRequest refreshRequest = new WebTokenRefreshRequest();
|
|
|
|
|
refreshRequest.userId = username;
|
|
|
|
|
refreshRequest.refreshToken = accessToken.refresh_token;
|
|
|
|
|
logger.debug("refresh token: {}", accessToken.refresh_token);
|
|
|
|
|
Map<String, Object> parameters =
|
|
|
|
|
Collections.singletonMap("grant_type", "refresh_token");
|
|
|
|
|
HttpResponse<String> response =
|
|
|
|
|
httpPost(
|
|
|
|
|
"oauth2",
|
|
|
|
|
OWSEC_SERVICE,
|
|
|
|
|
refreshRequest,
|
|
|
|
|
parameters
|
|
|
|
|
);
|
|
|
|
|
if (!response.isSuccess()) {
|
|
|
|
|
logger.error(
|
|
|
|
|
"Failed to refresh token: Response code {}, body: {}",
|
|
|
|
|
response.getStatus(),
|
|
|
|
|
response.getBody()
|
|
|
|
|
);
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
try {
|
|
|
|
|
return gson.fromJson(response.getBody(), WebTokenResult.class);
|
|
|
|
|
} catch (JsonSyntaxException e) {
|
|
|
|
|
logger.error(
|
|
|
|
|
"Failed to serialize WebTokenResult: Unexpected response:",
|
|
|
|
|
e
|
|
|
|
|
);
|
|
|
|
|
logger.debug("Response body: {}", response.getBody());
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** Read system endpoint URLs from uCentralSec. */
|
|
|
|
|
private boolean loadSystemEndpoints() {
|
|
|
|
|
// Make request
|
|
|
|
|
@@ -324,8 +458,12 @@ public class UCentralClient {
|
|
|
|
|
.connectTimeout(connectTimeoutMs)
|
|
|
|
|
.socketTimeout(socketTimeoutMs);
|
|
|
|
|
if (usePublicEndpoints) {
|
|
|
|
|
if (accessToken != null) {
|
|
|
|
|
req.header("Authorization", "Bearer " + accessToken);
|
|
|
|
|
if (!isAccessTokenExpired()) {
|
|
|
|
|
req.header(
|
|
|
|
|
"Authorization",
|
|
|
|
|
"Bearer " + accessToken.access_token
|
|
|
|
|
);
|
|
|
|
|
lastAccess = Instant.now().getEpochSecond();
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
req
|
|
|
|
|
@@ -339,26 +477,29 @@ public class UCentralClient {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** Send a POST request with a JSON body. */
|
|
|
|
|
/** Send a POST request with a JSON body and query params. */
|
|
|
|
|
private HttpResponse<String> httpPost(
|
|
|
|
|
String endpoint,
|
|
|
|
|
String service,
|
|
|
|
|
Object body
|
|
|
|
|
Object body,
|
|
|
|
|
Map<String, Object> parameters
|
|
|
|
|
) {
|
|
|
|
|
return httpPost(
|
|
|
|
|
endpoint,
|
|
|
|
|
service,
|
|
|
|
|
body,
|
|
|
|
|
parameters,
|
|
|
|
|
socketParams.connectTimeoutMs,
|
|
|
|
|
socketParams.socketTimeoutMs
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** Send a POST request with a JSON body using given timeout values. */
|
|
|
|
|
/** Send a POST request with a JSON body and query params using given timeout values. */
|
|
|
|
|
private HttpResponse<String> httpPost(
|
|
|
|
|
String endpoint,
|
|
|
|
|
String service,
|
|
|
|
|
Object body,
|
|
|
|
|
Map<String, Object> parameters,
|
|
|
|
|
int connectTimeoutMs,
|
|
|
|
|
int socketTimeoutMs
|
|
|
|
|
) {
|
|
|
|
|
@@ -367,9 +508,16 @@ public class UCentralClient {
|
|
|
|
|
.header("accept", "application/json")
|
|
|
|
|
.connectTimeout(connectTimeoutMs)
|
|
|
|
|
.socketTimeout(socketTimeoutMs);
|
|
|
|
|
if (parameters != null && !parameters.isEmpty()) {
|
|
|
|
|
req.queryString(parameters);
|
|
|
|
|
}
|
|
|
|
|
if (usePublicEndpoints) {
|
|
|
|
|
if (accessToken != null) {
|
|
|
|
|
req.header("Authorization", "Bearer " + accessToken);
|
|
|
|
|
if (!isAccessTokenExpired()) {
|
|
|
|
|
req.header(
|
|
|
|
|
"Authorization",
|
|
|
|
|
"Bearer " + accessToken.access_token
|
|
|
|
|
);
|
|
|
|
|
lastAccess = Instant.now().getEpochSecond();
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
req
|
|
|
|
|
@@ -454,6 +602,7 @@ public class UCentralClient {
|
|
|
|
|
String.format("device/%s/wifiscan", serialNumber),
|
|
|
|
|
OWGW_SERVICE,
|
|
|
|
|
req,
|
|
|
|
|
null,
|
|
|
|
|
socketParams.connectTimeoutMs,
|
|
|
|
|
socketParams.wifiScanTimeoutMs
|
|
|
|
|
);
|
|
|
|
|
@@ -482,7 +631,8 @@ public class UCentralClient {
|
|
|
|
|
HttpResponse<String> response = httpPost(
|
|
|
|
|
String.format("device/%s/configure", serialNumber),
|
|
|
|
|
OWGW_SERVICE,
|
|
|
|
|
req
|
|
|
|
|
req,
|
|
|
|
|
null
|
|
|
|
|
);
|
|
|
|
|
if (!response.isSuccess()) {
|
|
|
|
|
logger.error("Error: {}", response.getBody());
|
|
|
|
|
|