package com.battery.device.bts;
|
|
import java.util.Locale;
|
import java.util.Set;
|
import java.util.regex.Matcher;
|
import java.util.regex.Pattern;
|
|
/**
|
* 新威 BTS 对外接口的**只读命令白名单**(ADR-0068 §1)。
|
*
|
* <p>启用厂商对外接口等于同时打开了控制通道:厂商协议里 {@code start}/{@code stop}/{@code reset}/
|
* {@code broadcaststop} 与只读命令走同一条管道、同一套报文格式,服务端不做区分。
|
* 因此"只观测不控制"这条不变量在新威这条路上**不是服务端保证,而是代码纪律**,
|
* 落点就是这里:每条出站请求在发出前按 {@code <cmd>} 取值比对白名单,不在白名单内的一律拒绝。
|
*
|
* <p>为什么拦在传输层而不是各调用处:调用处会随需求增加,白名单只有一处才可能被审计。
|
* 独立成类而不写在传输类里,是为了让这条边界能被单独测到(不需要真的有一条管道)。
|
*/
|
final class NewareBtsReadOnlyCommands
|
{
|
/** 允许发送的命令,全部是只读命令;取值来自《BTS API技术协议》与现场实测(ADR-0068)。 */
|
private static final Set<String> ALLOWED = Set.of(
|
"getdevinfo", "getchlstatus", "inquire", "inquiredf", "download",
|
"downloadsteplayer", "downloadlog", "getparallel", "getpause");
|
|
private static final Pattern COMMAND = Pattern.compile("<cmd>\\s*([A-Za-z_][A-Za-z0-9_]*)\\s*</cmd>");
|
|
private NewareBtsReadOnlyCommands()
|
{
|
}
|
|
/**
|
* 校验请求只含白名单内的命令,返回解析出的命令名(小写)。
|
*
|
* <p>没有 {@code <cmd>} 的报文不是本协议的请求,同样拒绝——放行一个解析不出命令的报文,
|
* 等于把"这道门"的判定权交给厂商服务端。
|
*
|
* @param request 完整请求报文
|
* @return 请求里的命令名(小写)
|
* @throws BtsTransportException 缺少命令,或命令不在只读白名单内
|
*/
|
static String requireAllowed(String request)
|
{
|
Matcher matcher = request == null ? null : COMMAND.matcher(request);
|
if (matcher == null || !matcher.find())
|
throw new BtsTransportException(BtsTransportException.Reason.READ_ONLY_REJECTED,
|
"BTS 请求缺少 <cmd>,拒绝发出", null);
|
String command = matcher.group(1).toLowerCase(Locale.ROOT);
|
if (!ALLOWED.contains(command))
|
throw new BtsTransportException(BtsTransportException.Reason.READ_ONLY_REJECTED,
|
"BTS 只读白名单外的命令被拒绝:" + command, null);
|
return command;
|
}
|
}
|