yeg
2026-09-28 b9edba7f696b2175cc74f111891d9051ce5e9c31
拒绝把全权限账号写进厂商数据源配置

「全权限账号不进入配置」原本只是默认值与文档约定。现在装配时直接拒绝现场已知的全权限
账号(BtsServer、root),只读账号仍默认取 visitor,让这条要求成为结构性事实而不是纪律约定。
真正的只读边界仍是服务端授权与语句白名单两层,这一层只挡住误配。
2个文件已修改
36 ■■■■■ 已修改文件
battery-device-neware/src/main/java/com/battery/device/neware/NewareMysqlAdapterFactory.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
battery-device-neware/src/test/java/com/battery/device/neware/NewareMysqlAdapterContractTest.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
battery-device-neware/src/main/java/com/battery/device/neware/NewareMysqlAdapterFactory.java
@@ -1,5 +1,7 @@
package com.battery.device.neware;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import com.battery.device.api.DeviceAdapter;
import com.battery.device.api.DeviceAdapterException;
@@ -21,6 +23,8 @@
    public static final String PASSWORD_ENV_OPTION = "passwordEnv";
    private static final String DEFAULT_DATABASE = "bts63";
    private static final String READ_ONLY_USER = "visitor";
    /** 现场已知的全权限账号;写进交付配置就等于把误写风险带进现场。 */
    private static final List<String> FULL_PRIVILEGE_USERS = List.of("btsserver", "root");
    @Override
    public String adapterType()
@@ -32,7 +36,7 @@
    public DeviceAdapter create(AdapterInstanceConfig config)
    {
        String database = option(config.sourceConfig(), DATABASE_OPTION, DEFAULT_DATABASE);
        String user = option(config.sourceConfig(), USER_OPTION, READ_ONLY_USER);
        String user = readOnlyUser(config.sourceConfig());
        String password = credential(option(config.sourceConfig(), PASSWORD_ENV_OPTION,
                NewareMysqlAdapter.DEFAULT_PASSWORD_ENV));
        SingleConnectionVendorDataSource source = new SingleConnectionVendorDataSource(
@@ -40,6 +44,20 @@
        return NewareMysqlAdapter.create(config, source);
    }
    /**
     * 解析厂商库账号:默认取现场取得的服务端只读账号,已知的全权限账号一律拒绝。
     *
     * <p>准入只按账号名拦已知的全权限账号(那不是"写一次就回不去"的能力),
     * 真正的只读边界仍是服务端授权与语句白名单两层;这一层只是不把误配写进交付配置。
     */
    static String readOnlyUser(Map<String, String> sourceConfig)
    {
        String user = option(sourceConfig, USER_OPTION, READ_ONLY_USER);
        if (FULL_PRIVILEGE_USERS.contains(user.toLowerCase(Locale.ROOT)))
            throw new DeviceAdapterException("厂商数据源只允许只读账号,拒绝全权限账号:" + user);
        return user;
    }
    /** 只读账号口令;启动参数优先于环境变量,便于本机排查而不必改环境。 */
    static String credential(String variableName)
    {
battery-device-neware/src/test/java/com/battery/device/neware/NewareMysqlAdapterContractTest.java
@@ -204,4 +204,20 @@
        assertTrue(adapter.discoverChannels().stream()
                .allMatch(item -> ADAPTER_CODE.equals(item.identity().adapterCode())));
    }
    /** 凭据只用现场取得的服务端只读账号;已知的全权限账号不进入配置。 */
    @Test
    void credentialsFallBackToTheReadOnlyAccountAndRefuseFullPrivilegeAccounts()
    {
        assertEquals("visitor", NewareMysqlAdapterFactory.readOnlyUser(Map.of()));
        assertEquals("visitor", NewareMysqlAdapterFactory.readOnlyUser(Map.of("user", " ")));
        assertEquals("another_ro", NewareMysqlAdapterFactory.readOnlyUser(Map.of("user", "another_ro")));
        for (String account : List.of("BtsServer", "btsserver", "root", "ROOT"))
        {
            var failure = org.junit.jupiter.api.Assertions.assertThrows(
                    com.battery.device.api.DeviceAdapterException.class,
                    () -> NewareMysqlAdapterFactory.readOnlyUser(Map.of("user", account)));
            assertTrue(failure.getMessage().contains(account));
        }
    }
}