节点在线、应用在线、配置在线使用令牌查询
大石头 authored at 2021-12-16 19:49:30
5.14 KiB
Stardust
using System;
using System.Net;
using System.Net.Http;
using System.Net.Http.Json;
using System.Threading.Tasks;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.TestHost;
using NewLife;
using NewLife.Remoting;
using NewLife.Remoting.Models;
using NewLife.Security;
using Stardust.Data;
using Stardust.Data.Monitors;
using Stardust.Server;
using Xunit;

namespace ServerTest.Controllers;

/// <summary>埋点上报鉴权安全测试。验证匿名上报默认被拒、令牌不能冒名其它应用灌数据(安全扫描 V-9)</summary>
public class TraceSecurityTests
{
    private readonly TestServer _server;

    public TraceSecurityTests()
    {
#pragma warning disable CS0618, ASPDEPR008
        _server = new TestServer(WebHost.CreateDefaultBuilder()
            .UseStartup<Startup>());
#pragma warning restore CS0618, ASPDEPR008
    }

    private async Task<String> AppLoginTokenAsync(String appId, String secret)
    {
        var client = _server.CreateClient();
        var rs = await client.PostAsync<LoginResponse>("app/login", new { AppId = appId, Secret = secret });
        Assert.NotNull(rs?.Token);
        return rs.Token;
    }

    private async Task<String> ReportAsync(String appId, String token)
    {
        var client = _server.CreateClient();
        if (!token.IsNullOrEmpty())
            client.DefaultRequestHeaders.Add("X-Token", token);

        var model = new { AppId = appId, ClientId = "test-client" };
        var response = await client.PostAsync("/Trace/Report", JsonContent.Create(model));
        return await response.Content.ReadAsStringAsync();
    }

    private App CreateApp(String name, String secret)
    {
        var app = new App { Name = name, Enable = true };
        if (!secret.IsNullOrEmpty()) app.Secret = secret;
        app.Insert();

        // 埋点上报需要已存在的 AppTracer(Enable=true)
        new AppTracer { Name = name, AppId = app.Id, Enable = true }.Insert();
        return app;
    }

    [Fact(DisplayName = "匿名埋点上报默认被拒绝")]
    public async Task AnonymousReport_Rejected()
    {
        var app = CreateApp("trx_" + Rand.NextString(8), "trx-secret");
        try
        {
            var body = await ReportAsync(app.Name, "");

            Assert.Contains("\"code\"", body);
            Assert.Contains("匿名", body);
        }
        finally
        {
            AppTracer.FindByName(app.Name)?.Delete();
            app.Delete();
        }
    }

    [Fact(DisplayName = "应用令牌不能冒名其它应用上报")]
    public async Task Token_CannotReportAsOtherApp()
    {
        var a = CreateApp("trx_" + Rand.NextString(8), "trx-secret-a");
        var b = CreateApp("try_" + Rand.NextString(8), "trx-secret-b");
        try
        {
            var token = await AppLoginTokenAsync(a.Name, "trx-secret-a");

            var body = await ReportAsync(b.Name, token);

            Assert.Contains("\"code\"", body);
            Assert.Contains("不一致", body);
        }
        finally
        {
            AppTracer.FindByName(a.Name)?.Delete();
            AppTracer.FindByName(b.Name)?.Delete();
            a.Delete();
            b.Delete();
        }
    }

    [Fact(DisplayName = "应用令牌上报自身埋点正常(回归保护)")]
    public async Task Token_ReportSelf_Ok()
    {
        var app = CreateApp("trz_" + Rand.NextString(8), "trz-secret");
        try
        {
            var token = await AppLoginTokenAsync(app.Name, "trz-secret");

            var body = await ReportAsync(app.Name, token);

            Assert.DoesNotContain("匿名", body);
            Assert.DoesNotContain("不一致", body);
        }
        finally
        {
            AppTracer.FindByName(app.Name)?.Delete();
            app.Delete();
        }
    }

    [Fact(DisplayName = "注册应用改名后,监控身份显示名跟随同步")]
    public async Task AppRename_TracerDisplayNameFollows()
    {
        var name = "trf_" + Rand.NextString(8);
        var app = CreateApp(name, "trf-secret");
        try
        {
            var token = await AppLoginTokenAsync(name, "trf-secret");

            // 注册应用设置显示名
            app.DisplayName = "旧名字";
            app.Update();

            await ReportAsync(name, token);
            Assert.Equal("旧名字", AppTracer.FindByName(name)?.DisplayName);

            // 注册应用改名后再次上报,监控身份应跟随新名字
            app.DisplayName = "新名字";
            app.Update();

            await ReportAsync(name, token);
            Assert.Equal("新名字", AppTracer.FindByName(name)?.DisplayName);
        }
        finally
        {
            AppTracer.FindByName(name)?.Delete();
            app.Delete();
        }
    }

    [Fact(DisplayName = "SetHotApp 公开接口已下线")]
    public async Task SetHotApp_EndpointRemoved()
    {
        // 热门应用通知已改为跨应用事件总线,原公开接口删除后不应再可访问
        var client = _server.CreateClient();

        var response = await client.PostAsync("/Trace/SetHotApp", JsonContent.Create(1));

        Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
    }
}