using IM.Admin.Api; using IM.Admin.Data; using IM.Admin.Services; using IM.InitCommon.Management; using FileService.Infrastructure.Storage; using Microsoft.AspNetCore.DataProtection; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.DependencyInjection; using System.Text.Json.Nodes; using Xunit; namespace IM.Admin.Tests; public class PolicyTests { static readonly IConfiguration Config = new ConfigurationBuilder().AddInMemoryCollection(new Dictionary { ["Management:CredentialKey"] = Convert.ToBase64String(new byte[32]) }).Build(); [Fact] public void Defaults_preserve_unlimited_legacy_rules_and_explicit_report_limits() { foreach (var id in SettingsService.Fields.Keys) SettingsService.Validate(id, SettingsService.Defaults(id)); var p = new Policy(); Assert.True(p.RegistrationEnabled); Assert.Equal(0,p.FriendLimit); Assert.Equal(0,p.UploadMaxBytes); Assert.Equal(0,p.RecallMinutes); Assert.Equal(20,p.ReportsPerDay); Assert.Equal(10,p.ReportCooldownMinutes); } [Fact] public void Validation_rejects_unknown_fields_negative_limits_and_malformed_extensions() { var p = SettingsService.Defaults("social"); p["friendLimit"] = -1; Assert.Throws(() => SettingsService.Validate("social",p)); p = SettingsService.Defaults("account"); p["password"] = "must not be persisted"; Assert.Throws(() => SettingsService.Validate("account",p)); p = SettingsService.Defaults("messaging"); p["allowedFileTypes"] = new JsonArray("*.exe"); Assert.Throws(() => SettingsService.Validate("messaging",p)); } [Fact] public void Credentials_are_authenticated_encrypted_and_require_the_original_deployment_key() { using var db = new AdminDb(new DbContextOptionsBuilder().Options); var service = new SettingsService(db,new EphemeralDataProtectionProvider(),Config); var one = service.Protect("private-secret"); var two = service.Protect("private-secret"); Assert.NotEqual(one,two); Assert.DoesNotContain("private-secret",one); Assert.Equal("private-secret",service.Unprotect(one)); var wrong = new SettingsService(db,new EphemeralDataProtectionProvider(),new ConfigurationBuilder().AddInMemoryCollection(new Dictionary { ["Management:CredentialKey"] = Convert.ToBase64String(Enumerable.Repeat((byte)1,32).ToArray()) }).Build()); Assert.ThrowsAny(() => wrong.Unprotect(one)); } [Fact] public void Local_storage_paths_cannot_escape_the_managed_root() { var root = Path.Combine(Path.GetTempPath(),"im-storage-test"); Assert.StartsWith(Path.GetFullPath(root),LocalStorageAdapter.SafePath(root,"private","2026/file.txt")); Assert.Throws(() => LocalStorageAdapter.SafePath(root,"..","outside.txt")); Assert.Throws(() => LocalStorageAdapter.SafePath(root,Path.GetFullPath(Path.Combine(root,"..","outside.txt")))); } }