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

namespace ServerTest.Controllers;

/// <summary>OpenAI 兼容接口鉴权测试。覆盖未携带令牌、应用禁用与平台未启用三种拒绝路径</summary>
[Collection(StarTestServerCollection.Name)]
public class OpenAIChatSecurityTests : StarTestBase
{
    public OpenAIChatSecurityTests(StarTestServer fixture) : base(fixture) { }

    private static String ExtractToken(String body)
    {
        var m = Regex.Match(body, "\"token\"\\s*:\\s*\"([^\"]+)\"");
        return m.Success ? m.Groups[1].Value : null;
    }

    private static OpenAIChatRequest NewRequest() => new()
    {
        Messages = [new OpenAIChatMessage { Content = "hello" }],
    };

    [Fact(DisplayName = "未携带令牌调用OpenAI接口返回401错误体")]
    public async Task Chat_WithoutToken_Rejected()
    {
        var client = _server.CreateClient();

        var resp = await client.PostAsync("v1/chat/completions", JsonContent.Create(NewRequest()));
        var body = await resp.Content.ReadAsStringAsync();

        Assert.Equal(HttpStatusCode.Unauthorized, resp.StatusCode);
        Assert.Contains("\"error\"", body);
        Assert.Contains("authentication_error", body);
    }

    [Fact(DisplayName = "应用禁用后调用OpenAI接口返回403")]
    public async Task Chat_DisabledApp_Rejected()
    {
        var name = "ai_" + Rand.NextString(8);
        var app = new App { Name = name, Secret = "secret-" + Rand.NextString(8), Enable = true };
        app.Insert();
        try
        {
            var client = _server.CreateClient();

            var loginResp = await client.PostAsync("app/login", JsonContent.Create(new { AppId = name, Secret = app.Secret }));
            var token = ExtractToken(await loginResp.Content.ReadAsStringAsync());
            Assert.False(token.IsNullOrEmpty(), "登录应下发令牌");

            app.Enable = false;
            app.Update();

            client.DefaultRequestHeaders.Authorization = new("Bearer", token);
            var resp = await client.PostAsync("v1/chat/completions", JsonContent.Create(NewRequest()));
            var body = await resp.Content.ReadAsStringAsync();

            Assert.Equal(HttpStatusCode.Forbidden, resp.StatusCode);
            Assert.Contains("permission_error", body);
        }
        finally
        {
            app.Delete();
        }
    }

    [Fact(DisplayName = "平台未启用AI时OpenAI接口返回403")]
    public async Task Chat_AIDisabled_Rejected()
    {
        var set = StarServerSetting.Current;
        var old = set.EnableAI;

        var name = "ai_" + Rand.NextString(8);
        var app = new App { Name = name, Secret = "secret-" + Rand.NextString(8), Enable = true };
        app.Insert();
        try
        {
            set.EnableAI = false;

            var client = _server.CreateClient();

            var loginResp = await client.PostAsync("app/login", JsonContent.Create(new { AppId = name, Secret = app.Secret }));
            var token = ExtractToken(await loginResp.Content.ReadAsStringAsync());
            Assert.False(token.IsNullOrEmpty(), "登录应下发令牌");

            client.DefaultRequestHeaders.Authorization = new("Bearer", token);
            var resp = await client.PostAsync("v1/chat/completions", JsonContent.Create(NewRequest()));
            var body = await resp.Content.ReadAsStringAsync();

            Assert.Equal(HttpStatusCode.Forbidden, resp.StatusCode);
            Assert.Contains("\"error\"", body);
            Assert.False(body.IsNullOrWhiteSpace());
        }
        finally
        {
            set.EnableAI = old;
            app.Delete();
        }
    }
}