public async Task Save_Clean_OnlineConfig_ForSpecifiedUsers(string?expectedResult, params string[] selectedUsernames) { var settings = new Settings(); var directory = FileHelper.GetAbsolutePath(settings.OnlineConfigOutputDirectory); using var nodes = new Nodes(); var users = new Users(); users.AddUser("root"); users.AddUser("http"); users.AddUser("nobody"); // Constant interpolated strings is a preview feature. if (expectedResult is not null) { expectedResult = $"{expectedResult}{Environment.NewLine}"; } settings.OnlineConfigDeliverByGroup = false; // Save var genResult = await SIP008StaticGen.GenerateAndSave(users, nodes, settings, default, selectedUsernames);
public void Activate_Deactivate_Nodes_OnlineConfig_SSLinks() { var settings = new Settings(); using var nodes = new Nodes(); nodes.AddGroup("MyGroup"); nodes.AddGroup("MyGroupWithPlugin"); nodes.AddNodeToGroup("MyGroup", "MyNode", "github.com", 443); nodes.AddNodeToGroup("MyGroupWithPlugin", "MyNodeWithPlugin", "github.com", 443, "v2ray-plugin", "server;tls;host=github.com"); var users = new Users(); users.AddUser("root"); users.AddCredentialToUser("root", "MyGroup", "Y2hhY2hhMjAtaWV0Zi1wb2x5MTMwNTp5bWdoaVIjNzVUTnFwYQ"); users.AddCredentialToUser("root", "MyGroupWithPlugin", "aes-256-gcm", "wLhN2STZ"); var user = users.UserDict.First(); // Initial status: all activated var userOnlineConfigDict = SIP008StaticGen.GenerateForUser(user, users, nodes, settings); var userSsLinks = user.Value.GetSSUris(nodes); Assert.Equal(2, userOnlineConfigDict.First().Value.Servers.Count); Assert.Equal(2, userSsLinks.Count); // Deactivate first node nodes.Groups["MyGroup"].NodeDict["MyNode"].Deactivated = true; userOnlineConfigDict = SIP008StaticGen.GenerateForUser(user, users, nodes, settings); userSsLinks = user.Value.GetSSUris(nodes); Assert.Single(userOnlineConfigDict.First().Value.Servers); Assert.Single(userSsLinks); // Reactivate first node and deactivate second node nodes.Groups["MyGroup"].NodeDict["MyNode"].Deactivated = false; nodes.Groups["MyGroupWithPlugin"].NodeDict["MyNodeWithPlugin"].Deactivated = true; userOnlineConfigDict = SIP008StaticGen.GenerateForUser(user, users, nodes, settings); userSsLinks = user.Value.GetSSUris(nodes); Assert.Single(userOnlineConfigDict.First().Value.Servers); Assert.Single(userSsLinks); }
public static async Task <int> Generate(string[] usernames, CancellationToken cancellationToken = default) { var(users, loadUsersErrMsg) = await Users.LoadUsersAsync(cancellationToken); if (loadUsersErrMsg is not null) { Console.WriteLine(loadUsersErrMsg); return(1); } var(loadedNodes, loadNodesErrMsg) = await Nodes.LoadNodesAsync(cancellationToken); if (loadNodesErrMsg is not null) { Console.WriteLine(loadNodesErrMsg); return(1); } using var nodes = loadedNodes; var(settings, loadSettingsErrMsg) = await Settings.LoadSettingsAsync(cancellationToken); if (loadSettingsErrMsg is not null) { Console.WriteLine(loadSettingsErrMsg); return(1); } var errMsg = await SIP008StaticGen.GenerateAndSave(users, nodes, settings, cancellationToken, usernames); if (errMsg is not null) { Console.WriteLine(errMsg); return(1); } return(0); }
public static async Task <int> Remove(string[] usernames, CancellationToken cancellationToken = default) { var commandResult = 0; var(users, loadUsersErrMsg) = await Users.LoadUsersAsync(cancellationToken); if (loadUsersErrMsg is not null) { Console.WriteLine(loadUsersErrMsg); return(1); } var(loadedNodes, loadNodesErrMsg) = await Nodes.LoadNodesAsync(cancellationToken); if (loadNodesErrMsg is not null) { Console.WriteLine(loadNodesErrMsg); return(1); } using var nodes = loadedNodes; var(settings, loadSettingsErrMsg) = await Settings.LoadSettingsAsync(cancellationToken); if (loadSettingsErrMsg is not null) { Console.WriteLine(loadSettingsErrMsg); return(1); } // Removing online config requires reading user entry. if (settings.OnlineConfigCleanOnUserRemoval) { SIP008StaticGen.Remove(users, settings, usernames); } // Remove user entry. foreach (var username in usernames) { if (users.UserDict.TryGetValue(username, out var user)) { // Clear group and node ownership. var ownedGroupEntries = nodes.Groups.Where(x => x.Value.OwnerUuid == user.Uuid); var ownedNodeEntries = nodes.Groups.SelectMany(x => x.Value.NodeDict) .Where(x => x.Value.OwnerUuid == user.Uuid); foreach (var groupEntry in ownedGroupEntries) { groupEntry.Value.OwnerUuid = null; Console.WriteLine($"{username}: Disowned group {groupEntry.Key}."); } foreach (var nodeEntry in ownedNodeEntries) { nodeEntry.Value.OwnerUuid = null; Console.WriteLine($"{username}: Disowned node {nodeEntry.Key}."); } _ = users.RemoveUser(username); Console.WriteLine($"Removed user {username}."); } else { commandResult--; Console.WriteLine($"Error: User {username} doesn't exist."); } } var saveUsersErrMsg = await Users.SaveUsersAsync(users, cancellationToken); if (saveUsersErrMsg is not null) { Console.WriteLine(saveUsersErrMsg); return(1); } return(commandResult); }
public static async Task <int> Run( int interval, bool pullOutlineServer, bool deployOutlineServer, bool generateOnlineConfig, bool regenerateOnlineConfig, CancellationToken cancellationToken = default) { if (interval < 60 || interval > int.MaxValue / 1000) { Console.WriteLine($"Interval can't be shorter than 60 seconds or longer than {int.MaxValue / 1000} seconds."); return(1); } try { while (true) { var(users, loadUsersErrMsg) = await Users.LoadUsersAsync(cancellationToken); if (loadUsersErrMsg is not null) { Console.WriteLine(loadUsersErrMsg); return(1); } var(loadedNodes, loadNodesErrMsg) = await Nodes.LoadNodesAsync(cancellationToken); if (loadNodesErrMsg is not null) { Console.WriteLine(loadNodesErrMsg); return(1); } using var nodes = loadedNodes; var(settings, loadSettingsErrMsg) = await Settings.LoadSettingsAsync(cancellationToken); if (loadSettingsErrMsg is not null) { Console.WriteLine(loadSettingsErrMsg); return(1); } if (pullOutlineServer) { var errMsgs = nodes.PullFromOutlineServerForAllGroups(users, true, cancellationToken); await foreach (var errMsg in errMsgs) { Console.WriteLine(errMsg); } users.CalculateDataUsageForAllUsers(nodes); Console.WriteLine("Pulled from Outline servers."); } if (deployOutlineServer) { var errMsgs = nodes.DeployAllOutlineServers(users, cancellationToken); await foreach (var errMsg in errMsgs) { Console.WriteLine(errMsg); } Console.WriteLine("Deployed to Outline servers."); } if (generateOnlineConfig) { var errMsg = await SIP008StaticGen.GenerateAndSave(users, nodes, settings, cancellationToken); if (errMsg is not null) { Console.Write(errMsg); } Console.WriteLine("Generated online config."); } if (regenerateOnlineConfig) { SIP008StaticGen.Remove(users, settings); Console.WriteLine("Cleaned online config."); var errMsg = await SIP008StaticGen.GenerateAndSave(users, nodes, settings, cancellationToken); if (errMsg is not null) { Console.Write(errMsg); } Console.WriteLine("Generated online config."); } var saveUsersErrMsg = await Users.SaveUsersAsync(users, cancellationToken); if (saveUsersErrMsg is not null) { Console.WriteLine(saveUsersErrMsg); return(1); } var saveNodesErrMsg = await Nodes.SaveNodesAsync(nodes, cancellationToken); if (saveNodesErrMsg is not null) { Console.WriteLine(saveNodesErrMsg); return(1); } await Task.Delay(interval * 1000, cancellationToken); } } catch (OperationCanceledException) // Task.Delay() canceled { } catch (Exception ex) // other unhandled { Console.WriteLine($"An error occurred while executing one of the scheduled tasks: {ex.Message}"); return(-1); } return(0); }
public async Task Save_Clean_OnlineConfig_ForAllUsers() { var settings = new Settings(); var directory = FileHelper.GetAbsolutePath(settings.OnlineConfigOutputDirectory); using var nodes = new Nodes(); nodes.AddGroup("MyGroup"); nodes.AddGroup("MyGroupWithPlugin"); nodes.AddNodeToGroup("MyGroup", "MyNode", "github.com", 443); nodes.AddNodeToGroup("MyGroupWithPlugin", "MyNodeWithPlugin", "github.com", 443, "v2ray-plugin", "server;tls;host=github.com"); var users = new Users(); users.AddUser("root"); users.AddUser("http"); users.AddUser("nobody"); users.AddCredentialToUser("root", "MyGroup", "Y2hhY2hhMjAtaWV0Zi1wb2x5MTMwNTp5bWdoaVIjNzVUTnFwYQ"); users.AddCredentialToUser("http", "MyGroupWithPlugin", "aes-256-gcm", "wLhN2STZ"); var rootUser = users.UserDict["root"]; var httpUser = users.UserDict["http"]; var nobodyUser = users.UserDict["nobody"]; // Disable delivery by group settings.OnlineConfigDeliverByGroup = false; // Save var genResult = await SIP008StaticGen.GenerateAndSave(users, nodes, settings); Assert.Null(genResult); Assert.True(Directory.Exists(directory)); foreach (var user in users.UserDict.Values) { Assert.True(File.Exists($"{directory}/{user.Uuid}.json")); } // Clean SIP008StaticGen.Remove(users, settings); Assert.True(Directory.Exists(directory)); foreach (var user in users.UserDict.Values) { Assert.False(File.Exists($"{directory}/{user.Uuid}.json")); } // Delete working directory. Directory.Delete(directory); // Enable delivery by group settings.OnlineConfigDeliverByGroup = true; // Save var genByGroupResult = await SIP008StaticGen.GenerateAndSave(users, nodes, settings); Assert.Null(genByGroupResult); Assert.True(Directory.Exists(directory)); foreach (var user in users.UserDict.Values) { Assert.True(File.Exists($"{directory}/{user.Uuid}.json")); } Assert.True(Directory.Exists($"{directory}/{rootUser.Uuid}")); Assert.True(File.Exists($"{directory}/{rootUser.Uuid}/MyGroup.json")); Assert.False(File.Exists($"{directory}/{rootUser.Uuid}/MyGroupWithPlugin.json")); Assert.True(Directory.Exists($"{directory}/{httpUser.Uuid}")); Assert.False(File.Exists($"{directory}/{httpUser.Uuid}/MyGroup.json")); Assert.True(File.Exists($"{directory}/{httpUser.Uuid}/MyGroupWithPlugin.json")); Assert.False(Directory.Exists($"{directory}/{nobodyUser.Uuid}")); // Clean SIP008StaticGen.Remove(users, settings); Assert.True(Directory.Exists(directory)); foreach (var user in users.UserDict.Values) { Assert.False(File.Exists($"{directory}/{user.Uuid}.json")); } Assert.False(Directory.Exists($"{directory}/{rootUser.Uuid}")); Assert.False(Directory.Exists($"{directory}/{httpUser.Uuid}")); Assert.False(Directory.Exists($"{directory}/{nobodyUser.Uuid}")); // Delete working directory. Directory.Delete(directory); }
public void Generate_OnlineConfig_Properties() { var settings = new Settings(); using var nodes = new Nodes(); nodes.AddGroup("MyGroup"); nodes.AddGroup("MyGroupWithPlugin"); var users = new Users(); users.AddUser("root"); users.AddCredentialToUser("root", "MyGroup", "Y2hhY2hhMjAtaWV0Zi1wb2x5MTMwNTp5bWdoaVIjNzVUTnFwYQ"); users.AddCredentialToUser("root", "MyGroupWithPlugin", "aes-256-gcm", "wLhN2STZ"); var user = users.UserDict.First(); nodes.AddNodeToGroup("MyGroup", "MyNode", "github.com", 443); nodes.AddNodeToGroup("MyGroupWithPlugin", "MyNodeWithPlugin", "github.com", 443, "v2ray-plugin", "1.0", "server;tls;host=github.com", "-vvvvvv", user.Value.Uuid, "test"); var myNode = nodes.Groups["MyGroup"].NodeDict["MyNode"]; var myNodeWithPlugin = nodes.Groups["MyGroupWithPlugin"].NodeDict["MyNodeWithPlugin"]; settings.OnlineConfigDeliverByGroup = false; var userSingleOnlineConfigDict = SIP008StaticGen.GenerateForUser(user, users, nodes, settings); settings.OnlineConfigDeliverByGroup = true; var userPerGroupOnlineConfigDict = SIP008StaticGen.GenerateForUser(user, users, nodes, settings); // userSingleOnlineConfigDict Assert.Single(userSingleOnlineConfigDict); var userSingleOnlineConfig = userPerGroupOnlineConfigDict[user.Value.Uuid]; // userSingleOnlineConfig Assert.Equal(1, userSingleOnlineConfig.Version); Assert.Equal("root", userSingleOnlineConfig.Username); Assert.Equal(user.Value.Uuid, userSingleOnlineConfig.Id); Assert.Null(userSingleOnlineConfig.BytesUsed); Assert.Null(userSingleOnlineConfig.BytesRemaining); Assert.Equal(2, userSingleOnlineConfig.Servers.Count); var singleServer = userSingleOnlineConfig.Servers[0]; Assert.Equal(myNode.Uuid, singleServer.Id); Assert.Equal("MyNode", singleServer.Name); Assert.Equal("github.com", singleServer.Host); Assert.Equal(443, singleServer.Port); Assert.Equal("chacha20-ietf-poly1305", singleServer.Method); Assert.Equal("ymghiR#75TNqpa", singleServer.Password); Assert.Null(singleServer.PluginName); Assert.Null(singleServer.PluginVersion); Assert.Null(singleServer.PluginOptions); Assert.Null(singleServer.PluginArguments); Assert.Equal("MyGroup", singleServer.Group); Assert.Null(singleServer.Owner); Assert.Null(singleServer.Tags); var singleServerWithPlugin = userSingleOnlineConfig.Servers[1]; Assert.Equal(myNodeWithPlugin.Uuid, singleServerWithPlugin.Id); Assert.Equal("MyNodeWithPlugin", singleServerWithPlugin.Name); Assert.Equal("github.com", singleServerWithPlugin.Host); Assert.Equal(443, singleServerWithPlugin.Port); Assert.Equal("aes-256-gcm", singleServerWithPlugin.Method); Assert.Equal("wLhN2STZ", singleServerWithPlugin.Password); Assert.Equal("v2ray-plugin", singleServerWithPlugin.PluginName); Assert.Equal("1.0", singleServerWithPlugin.PluginVersion); Assert.Equal("server;tls;host=github.com", singleServerWithPlugin.PluginOptions); Assert.Equal("-vvvvvv", singleServerWithPlugin.PluginArguments); Assert.Equal("root", singleServerWithPlugin.Owner); Assert.Single(singleServerWithPlugin.Tags, "test"); // userPerGroupOnlineConfigDict Assert.Equal(3, userPerGroupOnlineConfigDict.Count); var userOnlineConfig = userPerGroupOnlineConfigDict[user.Value.Uuid]; var userMyGroupOnlineConfig = userPerGroupOnlineConfigDict[$"{user.Value.Uuid}/MyGroup"]; var userMyGroupWithPluginOnlineConfig = userPerGroupOnlineConfigDict[$"{user.Value.Uuid}/MyGroupWithPlugin"]; // userOnlineConfig Assert.Equal(1, userOnlineConfig.Version); Assert.Equal("root", userOnlineConfig.Username); Assert.Equal(user.Value.Uuid, userOnlineConfig.Id); Assert.Null(userOnlineConfig.BytesUsed); Assert.Null(userOnlineConfig.BytesRemaining); Assert.Equal(2, userOnlineConfig.Servers.Count); var server = userOnlineConfig.Servers[0]; Assert.Equal(myNode.Uuid, server.Id); Assert.Equal("MyNode", server.Name); Assert.Equal("github.com", server.Host); Assert.Equal(443, server.Port); Assert.Equal("chacha20-ietf-poly1305", server.Method); Assert.Equal("ymghiR#75TNqpa", server.Password); Assert.Null(server.PluginName); Assert.Null(server.PluginVersion); Assert.Null(server.PluginOptions); Assert.Null(server.PluginArguments); Assert.Equal("MyGroup", server.Group); Assert.Null(server.Owner); Assert.Null(server.Tags); var serverWithPlugin = userOnlineConfig.Servers[1]; Assert.Equal(myNodeWithPlugin.Uuid, serverWithPlugin.Id); Assert.Equal("MyNodeWithPlugin", serverWithPlugin.Name); Assert.Equal("github.com", serverWithPlugin.Host); Assert.Equal(443, serverWithPlugin.Port); Assert.Equal("aes-256-gcm", serverWithPlugin.Method); Assert.Equal("wLhN2STZ", serverWithPlugin.Password); Assert.Equal("v2ray-plugin", serverWithPlugin.PluginName); Assert.Equal("1.0", serverWithPlugin.PluginVersion); Assert.Equal("server;tls;host=github.com", serverWithPlugin.PluginOptions); Assert.Equal("-vvvvvv", serverWithPlugin.PluginArguments); Assert.Equal("root", serverWithPlugin.Owner); Assert.Single(serverWithPlugin.Tags, "test"); // userMyGroupOnlineConfig Assert.Equal(1, userMyGroupOnlineConfig.Version); Assert.Equal("root", userMyGroupOnlineConfig.Username); Assert.Equal(user.Value.Uuid, userMyGroupOnlineConfig.Id); Assert.Null(userMyGroupOnlineConfig.BytesUsed); Assert.Null(userMyGroupOnlineConfig.BytesRemaining); Assert.Single(userMyGroupOnlineConfig.Servers); var serverMyGroup = userMyGroupOnlineConfig.Servers[0]; Assert.Equal(myNode.Uuid, serverMyGroup.Id); Assert.Equal("MyNode", serverMyGroup.Name); Assert.Equal("github.com", serverMyGroup.Host); Assert.Equal(443, serverMyGroup.Port); Assert.Equal("chacha20-ietf-poly1305", serverMyGroup.Method); Assert.Equal("ymghiR#75TNqpa", serverMyGroup.Password); Assert.Null(serverMyGroup.PluginName); Assert.Null(serverMyGroup.PluginVersion); Assert.Null(serverMyGroup.PluginOptions); Assert.Null(serverMyGroup.PluginArguments); Assert.Equal("MyGroup", serverMyGroup.Group); Assert.Null(serverMyGroup.Owner); Assert.Null(serverMyGroup.Tags); // userMyGroupWithPluginOnlineConfig Assert.Equal(1, userMyGroupWithPluginOnlineConfig.Version); Assert.Equal("root", userMyGroupWithPluginOnlineConfig.Username); Assert.True(Guid.TryParse(userMyGroupWithPluginOnlineConfig.Id, out _)); Assert.Single(userMyGroupWithPluginOnlineConfig.Servers); var serverMyGroupWithPlugin = userMyGroupWithPluginOnlineConfig.Servers[0]; Assert.Equal(myNodeWithPlugin.Uuid, serverMyGroupWithPlugin.Id); Assert.Equal("MyNodeWithPlugin", serverMyGroupWithPlugin.Name); Assert.Equal("github.com", serverMyGroupWithPlugin.Host); Assert.Equal(443, serverMyGroupWithPlugin.Port); Assert.Equal("aes-256-gcm", serverMyGroupWithPlugin.Method); Assert.Equal("wLhN2STZ", serverMyGroupWithPlugin.Password); Assert.Equal("v2ray-plugin", serverMyGroupWithPlugin.PluginName); Assert.Equal("1.0", serverMyGroupWithPlugin.PluginVersion); Assert.Equal("server;tls;host=github.com", serverMyGroupWithPlugin.PluginOptions); Assert.Equal("-vvvvvv", serverMyGroupWithPlugin.PluginArguments); Assert.Equal("root", serverMyGroupWithPlugin.Owner); Assert.Single(serverMyGroupWithPlugin.Tags, "test"); }