refactor(host): single-source ChannelPolicy, space-signed file grants, whole-file registration
- all authorization rules (owner/creator, per-channel read/write grants, who may remove whom, delete rules, structure management) live in ChannelPolicy; a test fails if they are reimplemented elsewhere - ChannelInfo carries what the caller may do (can_*), member and invitation lists say who can be removed; clients display it and never derive it - the host no longer stores or calls the file service: it only signs short-lived tickets from space permissions (create/put/get/delete/domain), and keeps an opaque fileDomain reference on the channel - a whole file is registered in one request (last frame header + per-frame length and digest) instead of one request per 1 MiB frame; the signed manifest still has to match every digest - drop file quota/threshold, receipts, reconcile and delete-queue code from the host - contract generator emits constants for enums, error codes, token roles, permissions and share states
This commit is contained in:
1 parent
ccb2c074d6
commit
17638b6e20
36 files changed
+926
-589
No files matched your search
@@ -0,0 +1,48 @@
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace WebDrop.Core.Tests;
|
||||
|
||||
/// <summary>
|
||||
/// 权限规则只写在 ChannelPolicy 一处:别的地方只许调用,不许再各写一遍。
|
||||
/// 这个测试扫描源码,发现在 ChannelPolicy 之外重新判断角色、读写授权就失败,防止重复实现长回来。
|
||||
/// </summary>
|
||||
public sealed class PolicySingleSourceTests
|
||||
{
|
||||
private static string SourceRoot()
|
||||
{
|
||||
var dir = new DirectoryInfo(AppContext.BaseDirectory);
|
||||
while (dir is not null && !Directory.Exists(Path.Combine(dir.FullName, "src", "WebDrop.Core"))) dir = dir.Parent;
|
||||
return Path.Combine(dir?.FullName ?? throw new InvalidOperationException("找不到 server/src"), "src");
|
||||
}
|
||||
|
||||
// 这些写法只能出现在 ChannelPolicy.cs(数据类的定义和令牌校验除外)
|
||||
private static readonly (string Name, Regex Pattern)[] Forbidden =
|
||||
[
|
||||
("直接读频道访问授权的读写位", new Regex(@"\.(ChatRead|ChatWrite|FilesRead|FilesUpload)\b")),
|
||||
("直接比较是不是所有者", new Regex(@"Role\s*[!=]=\s*Role\.Owner")),
|
||||
("自己拼「所有者或创建者」", new Regex(@"AccountName\s*==\s*\w+\.CreatorAccount")),
|
||||
];
|
||||
|
||||
// 令牌校验(凭据层,不是授权规则)和数据定义,允许出现
|
||||
private static readonly string[] Allowed =
|
||||
["ChannelPolicy.cs", "WorkspaceCrypto.cs", "RequestAuth.cs", "ShareService.cs"];
|
||||
|
||||
[Fact]
|
||||
public void AuthorizationRulesAreWrittenOnlyInChannelPolicy()
|
||||
{
|
||||
var offenders = new List<string>();
|
||||
foreach (var file in Directory.EnumerateFiles(SourceRoot(), "*.cs", SearchOption.AllDirectories))
|
||||
{
|
||||
if (file.Contains(Path.DirectorySeparatorChar + "obj" + Path.DirectorySeparatorChar) || file.Contains(Path.DirectorySeparatorChar + "bin" + Path.DirectorySeparatorChar)) continue;
|
||||
if (Allowed.Contains(Path.GetFileName(file))) continue;
|
||||
var lines = File.ReadAllLines(file);
|
||||
for (int i = 0; i < lines.Length; i++)
|
||||
{
|
||||
if (lines[i].TrimStart().StartsWith("//") || lines[i].TrimStart().StartsWith("///")) continue;
|
||||
foreach (var (name, pattern) in Forbidden)
|
||||
if (pattern.IsMatch(lines[i])) offenders.Add($"{Path.GetFileName(file)}:{i + 1} {name}: {lines[i].Trim()}");
|
||||
}
|
||||
}
|
||||
Assert.True(offenders.Count == 0, "这些地方在 ChannelPolicy 之外重新判断了权限,请改成调用 ChannelPolicy:\n" + string.Join("\n", offenders));
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user