namespace WebDrop.Core; /// 谁能做什么。规则只写在这里,服务和测试都调用它。 public static class ChannelPolicy { public static bool IsOwner(Principal p) => p.Role == Role.Owner; public static bool CanRead(Principal p) => p.Role >= Role.Read; /// 成员写入需要频道允许上传,且服务端持有写令牌哈希。 public static bool CanWrite(Channel channel, Principal p) => p.Role == Role.Owner || (p.Role == Role.Write && channel.Settings.MembersCanUpload && channel.WriteHash is not null); // ───── 空间所有者与频道访问授权(ChannelAccess):谁能读、写哪个频道的哪个数据域。其他地方只调用这里,不再各写一遍 /// 空间的所有者:持有所有者令牌的人,或者空间的创建账号 public static bool IsSpaceOwner(Channel space, Principal p) => p.Role == Role.Owner || p.AccountName == space.CreatorAccount; /// 某个账号在某个频道上的访问授权;没有记录返回 null public static ChannelAccess? AccessOf(CryptoState crypto, string? account, string channelId) => crypto.Access.Find(a => a.ChannelId == channelId && a.Username == account); /// 这条授权允许读这个数据域 public static bool CanReadDomain(ChannelAccess? access, CryptoDomain domain) => access is not null && (domain == CryptoDomain.Chat ? access.ChatRead : access.FilesRead); /// 这条授权允许写这个数据域(聊天域写消息,文件域上传) public static bool CanWriteDomain(ChannelAccess? access, CryptoDomain domain) => access is not null && (domain == CryptoDomain.Chat ? access.ChatWrite : access.FilesUpload); /// 读一个对象:能读它所在的数据域,并且对象不早于授权生效的版本(关闭历史可读时,新成员看不到加入前的内容) public static bool CanReadObject(ChannelAccess? access, CryptoDomain domain, long objectRevision) => CanReadDomain(access, domain) && objectRevision >= (domain == CryptoDomain.Chat ? access!.ChatFromRevision : access!.FilesFromRevision); /// 频道对这个账号可见:聊天或文件任一域能读 public static bool CanSeeChannel(ChannelAccess access) => access.ChatRead || access.FilesRead; /// 能碰这个频道的文件域:能读或能上传 public static bool CanUseFiles(ChannelAccess access) => access.FilesRead || access.FilesUpload; /// 能往频道里放东西:聊天或文件任一域能写 public static bool CanWriteAnyDomain(ChannelAccess? access) => access is not null && (access.ChatWrite || access.FilesUpload); /// 这个频道里能读某个数据域的账号 public static IEnumerable Readers(CryptoState crypto, string channelId, CryptoDomain domain) => crypto.Access.Where(a => a.ChannelId == channelId && CanReadDomain(a, domain)); /// /// 这个空间是否已经按授权记录来约束成员:加密空间有了密钥信封,明文空间有了授权记录或对象登记之后才约束。 /// 之前(刚建好、还没初始化)所有成员的行为和旧版一致。 /// public static bool GrantsEnforced(Channel space, CryptoState crypto) => space.Mode == ChannelMode.Enc ? crypto.Envelopes.Count > 0 : crypto.Access.Count > 0 || crypto.Objects.Count > 0; // ───── 空间权限(身份组授予)与结构管理 /// 有没有这项空间权限:所有者拥有全部,其余看身份组授予 public static bool HasPermission(WorkspaceState? state, Principal principal, string permission) => principal.Role == Role.Owner || principal.AccountName is { } account && state?.Roles.Any(r => r.Members.Contains(account) && r.Permissions.Contains(permission)) == true; /// 重命名频道、调整频道所属分组、管理分组(所有者,或被授予「管理频道和分组」) public static bool CanManageStructure(WorkspaceState? state, Principal p) => HasPermission(state, p, WorkspacePermission.ManageStructure); /// 移除成员、撤销邀请的权限(所有者,或被授予「移除成员」)。具体能移除谁见 public static bool CanRemoveMembers(WorkspaceState? state, Principal p) => HasPermission(state, p, WorkspacePermission.RemoveMembers); /// /// 谁能移除谁:所有者能移除除自己外的任何人;被授权的成员不能移除自己、空间主,也不能移除同样有移除权限的人。 /// 执行(RevokeInvitation)和给前端的可移除标记(MembersView)都用这一处。 /// public static bool CanRemoveMember(WorkspaceState? state, Principal acting, string target, bool targetIsOwner) { if (targetIsOwner) return false; if (acting.Role == Role.Owner) return true; return acting.AccountName is { } actor && actor != target && CanRemoveMembers(state, acting) && !CanRemoveMembers(state, new Principal(Role.Write, target)); } // 下面这几项目前都只有所有者:新建频道(涉及密钥)、文件夹、邀请、改空间设置。要放开时只改这里 public static bool CanCreateChannel(Principal p) => IsOwner(p); public static bool CanManageFolders(Principal p) => IsOwner(p); public static bool CanInvite(Principal p) => IsOwner(p); public static bool CanEditSettings(Principal p) => IsOwner(p); public static bool CanManageRoles(Principal p) => IsOwner(p); // ───── 删除条目 /// 能删任何人发的条目:所有者,或空间的删除策略是 anyone public static bool CanDeleteAnyItem(Channel channel, Principal p) => IsOwner(p) || p.Role >= Role.Read && channel.Settings.DeletePolicy == DeletePolicy.Anyone; /// 能删自己发的条目:所有者,或删除策略是 own / anyone public static bool CanDeleteOwnItem(Channel channel, Principal p) => IsOwner(p) || p.Role >= Role.Read && channel.Settings.DeletePolicy is DeletePolicy.Own or DeletePolicy.Anyone; /// 某个具体条目能不能删:能删任何人的,或者是自己的并且能删自己的 public static bool CanDeleteItem(Channel channel, Item item, Principal p) => CanDeleteAnyItem(channel, p) || item.AuthorAccount == p.AccountName && CanDeleteOwnItem(channel, p); }