using System.Text.RegularExpressions; namespace WebDrop.Core.Tests; /// /// 权限规则只写在 ChannelPolicy 一处:别的地方只许调用,不许再各写一遍。 /// 这个测试扫描源码,发现在 ChannelPolicy 之外重新判断角色、读写授权就失败,防止重复实现长回来。 /// 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(); 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)); } }