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

namespace ServerTest.Controllers;

/// <summary>OAuth令牌接口重注册模式测试。验证收敛窗口/开放模式放行存量错密钥应用,并在客户端携带正确密钥后收敛关窗</summary>
[Collection(StarTestServerCollection.Name)]
public class OAuthReRegisterTests : StarTestBase
{
    public OAuthReRegisterTests(StarTestServer fixture) : base(fixture) { }

    private static TokenInModel BuildModel(String name, String password) => new()
    {
        grant_type = "password",
        UserName = name,
        Password = password,
        ClientId = "oar@1",
    };

    private async Task<String> PostAsync(String name, String password)
    {
        var client = _server.CreateClient();
        var response = await client.PostAsync("oauth/token", JsonContent.Create(BuildModel(name, password)));

        return await response.Content.ReadAsStringAsync();
    }

    [Fact(DisplayName = "关闭模式错密钥被拒绝")]
    public async Task Off_WrongPassword_Rejected()
    {
        var name = "oar_" + Rand.NextString(8);
        var app = new App { Name = name, Secret = "oar-off-1", Enable = true, ReRegister = ReRegisterModes.Off };
        app.Insert();
        try
        {
            var body = await PostAsync(name, "wrong-password");

            Assert.Contains("非法访问应用", body);
        }
        finally
        {
            app.Delete();
        }
    }

    [Fact(DisplayName = "开放模式错密钥放行并下发密钥")]
    public async Task Open_WrongPassword_Allowed()
    {
        var name = "oar_" + Rand.NextString(8);
        var app = new App { Name = name, Secret = "oar-open-1", Enable = true, ReRegister = ReRegisterModes.Open };
        app.Insert();
        try
        {
            var body = await PostAsync(name, null);

            // 标准令牌字段保持,并额外携带密钥供客户端保存后收敛
            Assert.Contains("\"access_token\"", body);
            Assert.Contains("\"refresh_token\"", body);
            Assert.Contains("\"token_type\":\"JWT\"", body);
            Assert.Contains("\"secret\":\"oar-open-1\"", body);
            Assert.Equal(ReRegisterModes.Open, App.FindByName(name)?.ReRegister);

            // 旧客户端仍可正常反序列化标准字段,忽略额外的secret字段
            var client = _server.CreateClient();
            var model = await client.PostAsync<TokenModel>("oauth/token", BuildModel(name, null));
            Assert.NotEmpty(model.AccessToken);
            Assert.NotEmpty(model.RefreshToken);
        }
        finally
        {
            app.Delete();
        }
    }

    [Fact(DisplayName = "收敛窗口错密钥放行,携带正确密钥后自动关窗")]
    public async Task Auto_AllowThenConverge_Closed()
    {
        var name = "oar_" + Rand.NextString(8);
        var app = new App { Name = name, Secret = "oar-auto-1", Enable = true, ReRegister = ReRegisterModes.Auto };
        app.Insert();
        try
        {
            // 第一次:错误密钥按收敛窗口放行,随令牌下发密钥
            var body1 = await PostAsync(name, "wrong-password");
            Assert.Contains("\"secret\":\"oar-auto-1\"", body1);
            Assert.Equal(ReRegisterModes.Auto, App.FindByName(name)?.ReRegister);

            // 第二次:携带正确密钥,收敛窗口自动关闭,不再下发密钥
            var body2 = await PostAsync(name, "oar-auto-1");
            Assert.Contains("\"access_token\"", body2);
            Assert.DoesNotContain("\"secret\":", body2);
            Assert.Equal(ReRegisterModes.Off, App.FindByName(name)?.ReRegister);
        }
        finally
        {
            app.Delete();
        }
    }

    [Fact(DisplayName = "可信接入模式错密钥被拒绝并提示")]
    public async Task Trusted_WrongPassword_Rejected()
    {
        var name = "oar_" + Rand.NextString(8);
        var app = new App { Name = name, Secret = "oar-trust-1", Enable = true, ReRegister = ReRegisterModes.Trusted };
        app.Insert();
        try
        {
            var body = await PostAsync(name, "wrong-password");

            Assert.Contains("可信接入", body);
        }
        finally
        {
            app.Delete();
        }
    }

    [Fact(DisplayName = "空密钥应用在开放模式下放行")]
    public async Task Open_EmptySecret_Allowed()
    {
        var name = "oar_" + Rand.NextString(8);
        var app = new App { Name = name, Secret = "oar-empty-1", Enable = true, ReRegister = ReRegisterModes.Open };
        app.Insert();
        try
        {
            // 模拟存量脏数据:绕过 Valid 直接置空密钥
            App.Update("Secret=null", $"Name='{name}'");

            var body = await PostAsync(name, null);

            Assert.Contains("\"access_token\"", body);
        }
        finally
        {
            app.Delete();
        }
    }
}