diff --git a/TGCommandLine/Program.cs b/TGCommandLine/Program.cs index 738434571d..fb0e245d09 100644 --- a/TGCommandLine/Program.cs +++ b/TGCommandLine/Program.cs @@ -85,7 +85,7 @@ namespace TGCommandLine } else if (interactive && !saidSrvVersion) { - Console.WriteLine("Connectd to service version: " + currentInterface.GetService().Version()); + Console.WriteLine("Connectd to service version: " + currentInterface.GetServiceComponent().Version()); saidSrvVersion = true; } diff --git a/TGCommandLine/ServiceCommands.cs b/TGCommandLine/ServiceCommands.cs index efbc628e7d..a52cd40dec 100644 --- a/TGCommandLine/ServiceCommands.cs +++ b/TGCommandLine/ServiceCommands.cs @@ -1,11 +1,12 @@ using System; using System.Collections.Generic; using TGServiceInterface; +using TGServiceInterface.Components; namespace TGCommandLine { /// - /// Used for managing the + /// Used for managing the components /// class ServiceCommand : RootCommand { @@ -26,7 +27,7 @@ namespace TGCommandLine } /// - /// Command for calling + /// Command for calling /// class ServiceCreateInstanceCommand : ConsoleCommand { @@ -54,7 +55,7 @@ namespace TGCommandLine /// protected override ExitCode Run(IList parameters) { - var res = Interface.GetService().CreateInstance(parameters[0], parameters[1]); + var res = Interface.GetServiceComponent().CreateInstance(parameters[0], parameters[1]); if (res != null) { OutputProc(res); @@ -65,7 +66,7 @@ namespace TGCommandLine } /// - /// Command for calling + /// Command for calling /// class ServiceListInstancesCommand : ConsoleCommand { @@ -86,14 +87,14 @@ namespace TGCommandLine /// protected override ExitCode Run(IList parameters) { - foreach (var I in Interface.GetService().ListInstances()) + foreach (var I in Interface.GetServiceComponent().ListInstances()) OutputProc(String.Format("{0} ({1}):\t{2}{3}", I.Name, I.Path, I.Enabled ? "Online" : "Offline", I.Enabled ? String.Format(" ({0})", I.LoggingID) : "")); return ExitCode.Normal; } } /// - /// Command for calling + /// Command for calling /// class ServiceDetachInstanceCommand : ConsoleCommand { @@ -121,7 +122,7 @@ namespace TGCommandLine /// protected override ExitCode Run(IList parameters) { - var res = Interface.GetService().DetachInstance(parameters[0]); + var res = Interface.GetServiceComponent().DetachInstance(parameters[0]); if (res != null) { OutputProc(res); @@ -132,7 +133,7 @@ namespace TGCommandLine } /// - /// Command for calling + /// Command for calling /// class ServiceImportInstanceCommand : ConsoleCommand { @@ -160,7 +161,7 @@ namespace TGCommandLine /// protected override ExitCode Run(IList parameters) { - var res = Interface.GetService().ImportInstance(parameters[0]); + var res = Interface.GetServiceComponent().ImportInstance(parameters[0]); if (res != null) { OutputProc(res); @@ -171,7 +172,7 @@ namespace TGCommandLine } /// - /// Command for calling + /// Command for calling /// class ServicePythonPathCommand : ConsoleCommand { @@ -192,7 +193,7 @@ namespace TGCommandLine /// protected override ExitCode Run(IList parameters) { - var res = Interface.GetService().PythonPath(); + var res = Interface.GetServiceComponent().PythonPath(); if (res != null) { OutputProc(res); @@ -203,7 +204,7 @@ namespace TGCommandLine } /// - /// Command for calling + /// Command for calling /// class ServiceSetPythonPathCommand : ConsoleCommand { @@ -231,13 +232,13 @@ namespace TGCommandLine /// protected override ExitCode Run(IList parameters) { - Interface.GetService().SetPythonPath(parameters[0]); + Interface.GetServiceComponent().SetPythonPath(parameters[0]); return ExitCode.Normal; } } /// - /// Command for calling with a parameter + /// Command for calling with a parameter /// class ServiceEnableInstanceCommand : ConsoleCommand { @@ -265,7 +266,7 @@ namespace TGCommandLine /// protected override ExitCode Run(IList parameters) { - var res = Interface.GetService().SetInstanceEnabled(parameters[0], true); + var res = Interface.GetServiceComponent().SetInstanceEnabled(parameters[0], true); if (res != null) { OutputProc(res); @@ -276,7 +277,7 @@ namespace TGCommandLine } /// - /// Command for calling with a parameter + /// Command for calling with a parameter /// class ServiceDisableInstanceCommand : ConsoleCommand { @@ -304,7 +305,7 @@ namespace TGCommandLine /// protected override ExitCode Run(IList parameters) { - var res = Interface.GetService().SetInstanceEnabled(parameters[0], false); + var res = Interface.GetServiceComponent().SetInstanceEnabled(parameters[0], false); if (res != null) { OutputProc(res); @@ -336,7 +337,7 @@ namespace TGCommandLine /// protected override ExitCode Run(IList parameters) { - OutputProc(Interface.GetService().RemoteAccessPort().ToString()); + OutputProc(Interface.GetServiceComponent().RemoteAccessPort().ToString()); return ExitCode.Normal; } } @@ -381,7 +382,7 @@ namespace TGCommandLine return ExitCode.BadCommand; } - var res = Interface.GetService().SetRemoteAccessPort(port); + var res = Interface.GetServiceComponent().SetRemoteAccessPort(port); if (res != null) { OutputProc(res); @@ -393,7 +394,7 @@ namespace TGCommandLine } /// - /// Command for calling + /// Command for calling /// class ServiceRenameInstanceCommand : ConsoleCommand { @@ -421,7 +422,7 @@ namespace TGCommandLine /// protected override ExitCode Run(IList parameters) { - var res = Interface.GetService().RenameInstance(parameters[0], parameters[1]); + var res = Interface.GetServiceComponent().RenameInstance(parameters[0], parameters[1]); if (res != null) { OutputProc(res); diff --git a/TGControlPanel/ControlPanel/ControlPanel.cs b/TGControlPanel/ControlPanel/ControlPanel.cs index 7a7ead308e..015bfccae7 100644 --- a/TGControlPanel/ControlPanel/ControlPanel.cs +++ b/TGControlPanel/ControlPanel/ControlPanel.cs @@ -10,7 +10,7 @@ namespace TGControlPanel /// /// The main form /// - partial class ControlPanel : CountedForm + sealed partial class ControlPanel : CountedForm { /// /// List of instances being used by open control panels diff --git a/TGControlPanel/CountedForm.cs b/TGControlPanel/CountedForm.cs index 064c2f9b23..9b442e756e 100644 --- a/TGControlPanel/CountedForm.cs +++ b/TGControlPanel/CountedForm.cs @@ -5,7 +5,7 @@ namespace TGControlPanel /// /// Calls when all s are d /// - class CountedForm : Form + abstract class CountedForm : Form { /// /// The current number of active s diff --git a/TGControlPanel/InstanceSelector.Designer.cs b/TGControlPanel/InstanceSelector.Designer.cs index 4b07446a17..cfaf0a5ab5 100644 --- a/TGControlPanel/InstanceSelector.Designer.cs +++ b/TGControlPanel/InstanceSelector.Designer.cs @@ -36,6 +36,8 @@ this.RenameInstanceButton = new System.Windows.Forms.Button(); this.DetachInstanceButton = new System.Windows.Forms.Button(); this.RefreshButton = new System.Windows.Forms.Button(); + this.ConnectButton = new System.Windows.Forms.Button(); + this.EnabledCheckBox = new System.Windows.Forms.CheckBox(); this.SuspendLayout(); // // InstanceListBox @@ -46,6 +48,7 @@ this.InstanceListBox.Name = "InstanceListBox"; this.InstanceListBox.Size = new System.Drawing.Size(339, 238); this.InstanceListBox.TabIndex = 0; + this.InstanceListBox.SelectedIndexChanged += new System.EventHandler(this.InstanceListBox_SelectedIndexChanged); // // CreateInstanceButton // @@ -94,7 +97,7 @@ // RefreshButton // this.RefreshButton.Anchor = System.Windows.Forms.AnchorStyles.Right; - this.RefreshButton.Location = new System.Drawing.Point(358, 226); + this.RefreshButton.Location = new System.Drawing.Point(358, 195); this.RefreshButton.Name = "RefreshButton"; this.RefreshButton.Size = new System.Drawing.Size(148, 25); this.RefreshButton.TabIndex = 19; @@ -102,12 +105,37 @@ this.RefreshButton.UseVisualStyleBackColor = true; this.RefreshButton.Click += new System.EventHandler(this.RefreshButton_Click); // + // ConnectButton + // + this.ConnectButton.Anchor = System.Windows.Forms.AnchorStyles.Right; + this.ConnectButton.Location = new System.Drawing.Point(358, 226); + this.ConnectButton.Name = "ConnectButton"; + this.ConnectButton.Size = new System.Drawing.Size(148, 25); + this.ConnectButton.TabIndex = 20; + this.ConnectButton.Text = "Connect"; + this.ConnectButton.UseVisualStyleBackColor = true; + this.ConnectButton.Click += new System.EventHandler(this.ConnectButton_Click); + // + // EnabledCheckBox + // + this.EnabledCheckBox.AutoSize = true; + this.EnabledCheckBox.ForeColor = System.Drawing.Color.White; + this.EnabledCheckBox.Location = new System.Drawing.Point(396, 149); + this.EnabledCheckBox.Name = "EnabledCheckBox"; + this.EnabledCheckBox.Size = new System.Drawing.Size(65, 17); + this.EnabledCheckBox.TabIndex = 21; + this.EnabledCheckBox.Text = "Enabled"; + this.EnabledCheckBox.UseVisualStyleBackColor = true; + this.EnabledCheckBox.CheckedChanged += new System.EventHandler(this.EnabledCheckBox_CheckedChanged); + // // InstanceSelector // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(39)))), ((int)(((byte)(40)))), ((int)(((byte)(34))))); this.ClientSize = new System.Drawing.Size(518, 261); + this.Controls.Add(this.EnabledCheckBox); + this.Controls.Add(this.ConnectButton); this.Controls.Add(this.RefreshButton); this.Controls.Add(this.DetachInstanceButton); this.Controls.Add(this.RenameInstanceButton); @@ -119,6 +147,7 @@ this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; this.Text = "Server Instances"; this.ResumeLayout(false); + this.PerformLayout(); } @@ -130,5 +159,7 @@ private System.Windows.Forms.Button RenameInstanceButton; private System.Windows.Forms.Button DetachInstanceButton; private System.Windows.Forms.Button RefreshButton; + private System.Windows.Forms.Button ConnectButton; + private System.Windows.Forms.CheckBox EnabledCheckBox; } } \ No newline at end of file diff --git a/TGControlPanel/InstanceSelector.cs b/TGControlPanel/InstanceSelector.cs index 607227b771..238f422f04 100644 --- a/TGControlPanel/InstanceSelector.cs +++ b/TGControlPanel/InstanceSelector.cs @@ -3,13 +3,14 @@ using System.Collections.Generic; using System.Threading.Tasks; using System.Windows.Forms; using TGServiceInterface; +using TGServiceInterface.Components; namespace TGControlPanel { /// - /// Form used for managing manipulation functions + /// Form used for managing manipulation functions /// - partial class InstanceSelector : CountedForm + sealed partial class InstanceSelector : CountedForm { /// /// The we build instance connections from @@ -19,6 +20,15 @@ namespace TGControlPanel /// List of from /// IList InstanceData; + /// + /// Used for modifying without invoking its side effects + /// + bool UpdatingEnabledCheckbox = false; + + /// + /// Construct an + /// + /// An connected a the public InstanceSelector(IInterface I) { InitializeComponent(); @@ -47,15 +57,13 @@ namespace TGControlPanel } /// - /// Connects to a if it is double clicked in + /// Connects to a if it is double clicked in /// /// The sender of the event /// The void InstanceListBox_MouseDoubleClick(object sender, MouseEventArgs e) { - var index = InstanceListBox.IndexFromPoint(e.Location); - if (index != ListBox.NoMatches) - TryConnectToInstance(InstanceData[index].Name); + TryConnectToIndexInstance(InstanceListBox.IndexFromPoint(e.Location)); } /// @@ -77,25 +85,36 @@ namespace TGControlPanel } /// - /// Loads the using + /// Loads the using /// async void RefreshInstances() { InstanceListBox.Items.Clear(); await WrapServerOp(() => { - InstanceData = masterInterface.GetService().ListInstances(); + InstanceData = masterInterface.GetServiceComponent().ListInstances(); }); foreach(var I in InstanceData) InstanceListBox.Items.Add(String.Format("{0}: {1} - {2} - {3}", I.LoggingID, I.Name, I.Path, I.Enabled ? "ONLINE" : "OFFLINE")); + var HasServerAdmin = masterInterface.ConnectionStatus().HasFlag(ConnectivityLevel.Administrator); + CreateInstanceButton.Enabled = HasServerAdmin; + ImportInstanceButton.Enabled = HasServerAdmin; + RenameInstanceButton.Enabled = HasServerAdmin; + DetachInstanceButton.Enabled = HasServerAdmin; + EnabledCheckBox.Enabled = HasServerAdmin; + if(InstanceData.Count > 0) + InstanceListBox.SelectedIndex = 0; } /// - /// Tries to start a for a given + /// Tries to start a for a given /// - /// The name of the to connect to - async void TryConnectToInstance(string instanceName) + /// The of to connect to + async void TryConnectToIndexInstance(int index) { - if(ControlPanel.InstancesInUse.TryGetValue(instanceName, out ControlPanel activeCP)) + if (index == ListBox.NoMatches) + return; + var instanceName = InstanceData[index].Name; + if (ControlPanel.InstancesInUse.TryGetValue(instanceName, out ControlPanel activeCP)) { activeCP.BringToFront(); return; @@ -123,7 +142,7 @@ namespace TGControlPanel } /// - /// Prompts the user for parameters to + /// Prompts the user for parameters to /// /// The sender of the event /// The @@ -135,7 +154,7 @@ namespace TGControlPanel if (MessageBox.Show(String.Format("This will dissociate the server instance at \"{0}\"! Are you sure?", imd.Path), "Instance Detach", MessageBoxButtons.YesNo) != DialogResult.Yes) return; string res = null; - await WrapServerOp(() => { res = masterInterface.GetService().DetachInstance(imd.Name); }); + await WrapServerOp(() => res = masterInterface.GetServiceComponent().DetachInstance(imd.Name)); if (res != null) MessageBox.Show(res); RefreshInstances(); @@ -152,7 +171,7 @@ namespace TGControlPanel } /// - /// Prompts the user for parameters to + /// Prompts the user for parameters to /// /// The sender of the event /// The @@ -167,14 +186,14 @@ namespace TGControlPanel if (imd.Enabled && MessageBox.Show(String.Format("This will temporarily offline the server instance! Are you sure?", imd.Path), "Instance Restart", MessageBoxButtons.YesNo) != DialogResult.Yes) return; string res = null; - await WrapServerOp(() => { res = masterInterface.GetService().RenameInstance(imd.Name, new_name); }); + await WrapServerOp(() => res = masterInterface.GetServiceComponent().RenameInstance(imd.Name, new_name)); if (res != null) MessageBox.Show(res); RefreshInstances(); } /// - /// Prompts the user for parameters to + /// Prompts the user for parameters to /// /// The sender of the event /// The @@ -184,14 +203,14 @@ namespace TGControlPanel if (instance_path == null) return; string res = null; - await WrapServerOp(() => { res = masterInterface.GetService().ImportInstance(instance_path); }); + await WrapServerOp(() => res = masterInterface.GetServiceComponent().ImportInstance(instance_path)); if (res != null) MessageBox.Show(res); RefreshInstances(); } /// - /// Prompts the user for parameters to + /// Prompts the user for parameters to /// /// The sender of the event /// The @@ -204,10 +223,62 @@ namespace TGControlPanel if (instance_path == null) return; string res = null; - await WrapServerOp(() => { res = masterInterface.GetService().CreateInstance(instance_name, instance_path); }); + await WrapServerOp(() => res = masterInterface.GetServiceComponent().CreateInstance(instance_name, instance_path)); if (res != null) MessageBox.Show(res); RefreshInstances(); } + + /// + /// Attempts to connect the user to an based on the of + /// + /// The sender of the event + /// The + void ConnectButton_Click(object sender, EventArgs e) + { + TryConnectToIndexInstance(InstanceListBox.SelectedIndex); + } + + /// + /// Prompts the user if they want to call to either online or offline an based on its current state + /// + /// The sender of the event + /// The + async void EnabledCheckBox_CheckedChanged(object sender, EventArgs e) + { + if (UpdatingEnabledCheckbox) + return; + var enabling = EnabledCheckBox.Checked; + try + { + if (MessageBox.Show(String.Format("Are you sure you want to {0} this instance?", enabling ? "online" : "offline"), "Instance Status Change", MessageBoxButtons.YesNo) != DialogResult.Yes) + return; + string res = null; + var index = InstanceListBox.SelectedIndex; + await WrapServerOp(() => res = masterInterface.GetServiceComponent().SetInstanceEnabled(InstanceData[index].Name, enabling)); + if (res != null) + MessageBox.Show(res); + } + finally + { + RefreshInstances(); + } + } + + /// + /// Update based on the selected 's property + /// + /// The sender of the event + /// The + void InstanceListBox_SelectedIndexChanged(object sender, EventArgs e) + { + var index = InstanceListBox.SelectedIndex; + if (index != ListBox.NoMatches) + { + UpdatingEnabledCheckbox = true; + EnabledCheckBox.Checked = InstanceData[index].Enabled; + UpdatingEnabledCheckbox = false; + } + } } } diff --git a/TGControlPanel/Login.cs b/TGControlPanel/Login.cs index 75b1a508b3..364746a652 100644 --- a/TGControlPanel/Login.cs +++ b/TGControlPanel/Login.cs @@ -4,7 +4,7 @@ using TGServiceInterface; namespace TGControlPanel { - partial class Login : CountedForm + sealed partial class Login : CountedForm { /// /// Create a form @@ -73,28 +73,8 @@ namespace TGControlPanel MessageBox.Show("Authentication error: Username/password/windows identity is not authorized! Ensure you are a system administrator or in the correct Windows group on the service machine."); return; } - - if (!res.HasFlag(ConnectivityLevel.Administrator)) - { - while (true) - { - var InstanceToConnectTo = Program.TextPrompt("Select instance", "You do not have permission to list server instances. Please enter the name of the instance to connect to:"); - if (InstanceToConnectTo == null) - return; - - res = I.ConnectToInstance(InstanceToConnectTo); - if (!res.HasFlag(ConnectivityLevel.Connected)) - MessageBox.Show("Unable to connect to instance! Does it exist?"); - else if (!res.HasFlag(ConnectivityLevel.Authenticated)) - MessageBox.Show("The current user is not authorized to access this instance!"); - else - break; - } - - new ControlPanel(I).Show(); - } - else - new InstanceSelector(I).Show(); + + new InstanceSelector(I).Show(); Close(); } catch diff --git a/TGInstallerWrapper/Main.cs b/TGInstallerWrapper/Main.cs index 4fb2ae29a9..c796238fd3 100644 --- a/TGInstallerWrapper/Main.cs +++ b/TGInstallerWrapper/Main.cs @@ -73,7 +73,7 @@ namespace TGInstallerWrapper var verifiedConnection = Interface.ConnectionStatus().HasFlag(ConnectivityLevel.Administrator); try { - VersionLabel.Text = Interface.GetService().Version(); + VersionLabel.Text = Interface.GetServiceComponent().Version(); var isV0 = VersionLabel.Text.Contains("v3.0"); if (isV0) //OH GOD!!!! MessageBox.Show("Upgrading from version 3.0 may trigger a bug that can delete /config and /data. IT IS STRONGLY RECCOMMENDED THAT YOU BACKUP THESE FOLDERS BEFORE UPDATING!", "Warning"); @@ -98,7 +98,7 @@ namespace TGInstallerWrapper var connectionVerified = Interface.ConnectionStatus().HasFlag(ConnectivityLevel.Administrator); try { - Interface.GetService().PrepareForUpdate(); + Interface.GetServiceComponent().PrepareForUpdate(); Thread.Sleep(3000); //chat messages return true; } diff --git a/TGServerService/DeprecatedInstanceConfig.cs b/TGServerService/DeprecatedInstanceConfig.cs index 0c201ae057..9eff86cc04 100644 --- a/TGServerService/DeprecatedInstanceConfig.cs +++ b/TGServerService/DeprecatedInstanceConfig.cs @@ -20,7 +20,7 @@ namespace TGServerService public static IInstanceConfig CreateFromNETSettings() { var Config = Properties.Settings.Default; - var result = new DeprecatedInstanceConfig(LoadPreviousNetPropertyOrDefault("ServerDirectory", "C:\\tgstation-server-3")); + var result = new DeprecatedInstanceConfig(Program.NormalizePath(LoadPreviousNetPropertyOrDefault("ServerDirectory", "C:\\tgstation-server-3"))); // using nameof for sanity where possible result.ProjectName = LoadPreviousNetPropertyOrDefault(nameof(ProjectName), result.ProjectName); result.Port = LoadPreviousNetPropertyOrDefault("ServerPort", result.Port); diff --git a/TGServerService/Program.cs b/TGServerService/Program.cs index a9c208a629..5184c9f194 100644 --- a/TGServerService/Program.cs +++ b/TGServerService/Program.cs @@ -175,5 +175,17 @@ namespace TGServerService { return input.Replace("%", "%25").Replace("=", "%3d").Replace(";", "%3b").Replace("&", "%26").Replace("+", "%2b"); } + + /// + /// Normalizes different versions of a path + /// + /// The path to normalize + /// The normalized path + public static string NormalizePath(string path) + { + return Path.GetFullPath(new Uri(path).LocalPath) + .TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar) + .ToUpperInvariant(); + } } } diff --git a/TGServerService/AdministrativeAuthorizationManager.cs b/TGServerService/RootAuthorizationManager.cs similarity index 73% rename from TGServerService/AdministrativeAuthorizationManager.cs rename to TGServerService/RootAuthorizationManager.cs index e5aaf062d5..f3f45d0983 100644 --- a/TGServerService/AdministrativeAuthorizationManager.cs +++ b/TGServerService/RootAuthorizationManager.cs @@ -1,5 +1,7 @@ using System; +using System.Collections.Generic; using System.Diagnostics; +using System.Linq; using System.Security.Principal; using System.ServiceModel; using TGServiceInterface.Components; @@ -9,21 +11,23 @@ namespace TGServerService /// /// A used to determine only if the caller is an admin /// - sealed class AdministrativeAuthorizationManager : ServiceAuthorizationManager + sealed class RootAuthorizationManager : ServiceAuthorizationManager { string LastSeenUser; + public static readonly IList InstanceAuthManagers = new List(); protected override bool CheckAccessCore(OperationContext operationContext) { var contract = operationContext.EndpointDispatcher.ContractName; - if (contract == typeof(ITGConnectivity).Name) //always allow connectivity checks return true; - + var windowsIdent = operationContext.ServiceSecurityContext.WindowsIdentity; var wp = new WindowsPrincipal(windowsIdent); //first allow admins var authSuccess = wp.IsInRole(WindowsBuiltInRole.Administrator); + if(!authSuccess && contract == typeof(ITGLanding).Name) + return InstanceAuthManagers.FirstOrDefault(x => x.CheckAccess(operationContext)) != null; var user = windowsIdent.Name; if (LastSeenUser != user) diff --git a/TGServerService/ServerInstance/Administration.cs b/TGServerService/ServerInstance/Administration.cs index 87891795af..affe43a2b7 100644 --- a/TGServerService/ServerInstance/Administration.cs +++ b/TGServerService/ServerInstance/Administration.cs @@ -73,6 +73,7 @@ namespace TGServerService /// The name of the group allowed to access the if it could be found, otherwise string FindTheDroidsWereLookingFor(string search = null, bool useDomain = false) { + RootAuthorizationManager.InstanceAuthManagers.Add(this); //find the group that is authorized to use the tools var pc = new PrincipalContext(useDomain ? ContextType.Domain : ContextType.Machine); var groupName = search ?? Config.AuthorizedUserGroupSID; @@ -95,6 +96,14 @@ namespace TGServerService } return gp.Name; } + + /// + /// Cleans up the component + /// + void DisposeAdministration() + { + RootAuthorizationManager.InstanceAuthManagers.Remove(this); + } /// /// Called by WCF whenever a component call is made. Checks to see that the supplied user account has access to the requested component diff --git a/TGServerService/ServerInstance/ServerInstance.cs b/TGServerService/ServerInstance/ServerInstance.cs index ec239dcb80..a92406939d 100644 --- a/TGServerService/ServerInstance/ServerInstance.cs +++ b/TGServerService/ServerInstance/ServerInstance.cs @@ -51,6 +51,7 @@ namespace TGServerService DisposeByond(); DisposeRepo(); DisposeChat(); + DisposeAdministration(); Config.Save(); } diff --git a/TGServerService/Service.cs b/TGServerService/Service.cs index c4835b0a66..51e7149592 100644 --- a/TGServerService/Service.cs +++ b/TGServerService/Service.cs @@ -15,7 +15,7 @@ namespace TGServerService /// The windows service the application runs as /// [ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple, InstanceContextMode = InstanceContextMode.Single)] - class Service : ServiceBase, ITGSService, ITGConnectivity + class Service : ServiceBase, ITGSService, ITGConnectivity, ITGLanding, ITGInstanceManager { /// /// The logging ID used for events @@ -233,9 +233,9 @@ namespace TGServerService void SetupService() { serviceHost = CreateHost(this, Interface.MasterInterfaceName); - AddEndpoint(serviceHost, typeof(ITGSService)); - AddEndpoint(serviceHost, typeof(ITGConnectivity)); - serviceHost.Authorization.ServiceAuthorizationManager = new AdministrativeAuthorizationManager(); //only admins can diddle us + foreach (var I in Interface.ValidServiceInterfaces) + AddEndpoint(serviceHost, I); + serviceHost.Authorization.ServiceAuthorizationManager = new RootAuthorizationManager(); //only admins can diddle us } /// @@ -277,7 +277,7 @@ namespace TGServerService WriteEntry(String.Format("Instance at {0} has a duplicate name! Detaching...", I.Directory), EventID.InstanceInitializationFailure, EventLogEntryType.Error, LoggingID); pathsToRemove.Add(I.Directory); } - if (SetupInstance(I) == null) + if (I.Enabled && SetupInstance(I) == null) pathsToRemove.Add(I.Directory); else seenNames.Add(I.Name); @@ -331,7 +331,6 @@ namespace TGServerService { var datInstance = ((ServerInstance)hosts[config.Directory].SingletonInstance); WriteEntry(String.Format("Unable to start instance at path {0}. Has the same name as instance at path {1}. Detaching...", config.Directory, datInstance.ServerDirectory()), EventID.InstanceInitializationFailure, EventLogEntryType.Error, LoggingID); - Properties.Settings.Default.InstancePaths.Remove(config.Directory); return null; } if (!config.Enabled) @@ -350,7 +349,7 @@ namespace TGServerService var host = CreateHost(instance, String.Format("{0}/{1}", Interface.InstanceInterfaceName, instanceName)); hosts.Add(instanceName, host); - foreach (var J in Interface.ValidInterfaces) + foreach (var J in Interface.ValidInstanceInterfaces) AddEndpoint(host, J); host.Authorization.ServiceAuthorizationManager = instance; @@ -471,6 +470,7 @@ namespace TGServerService /// public string CreateInstance(string Name, string path) { + path = Program.NormalizePath(path); var res = CheckInstanceName(Name); if (res != null) return res; @@ -510,6 +510,8 @@ namespace TGServerService /// on success, error message on failure string SetupOneInstance(IInstanceConfig config) { + if (!config.Enabled) + return null; try { var host = SetupInstance(config); @@ -529,6 +531,7 @@ namespace TGServerService /// public string ImportInstance(string path) { + path = Program.NormalizePath(path); var Config = Properties.Settings.Default; lock (this) { @@ -587,24 +590,14 @@ namespace TGServerService { if (hostIsOnline) return null; - //now this is a bit awkward because we need to check each instance config for the one named Name - string LastCheckedConfig = null; - try - { - foreach (var ic in GetInstanceConfigs()) + foreach (var ic in GetInstanceConfigs()) + if (ic.Name == Name) { - if (ic.Name == Name) - { - path = ic.Directory; - ic.Enabled = true; - return SetupOneInstance(ic); - } + path = ic.Directory; + ic.Enabled = true; + ic.Save(); + return SetupOneInstance(ic); } - } - catch (Exception e) - { - return String.Format("An error occurred while checking instance config at {0}! Error: ", LastCheckedConfig, e.ToString()); - } return String.Format("Instance {0} does not exist!", Name); } else diff --git a/TGServerService/TGServerService.csproj b/TGServerService/TGServerService.csproj index 8f4b5939f6..71ec660335 100644 --- a/TGServerService/TGServerService.csproj +++ b/TGServerService/TGServerService.csproj @@ -95,7 +95,7 @@ - + diff --git a/TGServiceInterface/Components/Connectivity.cs b/TGServiceInterface/Components/Connectivity.cs index 81975e6f84..d3077d3dd9 100644 --- a/TGServiceInterface/Components/Connectivity.cs +++ b/TGServiceInterface/Components/Connectivity.cs @@ -13,12 +13,5 @@ namespace TGServiceInterface.Components /// [OperationContract] void VerifyConnection(); - - /// - /// Retrieve's the service's version - /// - /// The service's version - [OperationContract] - string Version(); } } diff --git a/TGServiceInterface/Components/Instance.cs b/TGServiceInterface/Components/Instance.cs index 9172aee46b..c4c771d4ef 100644 --- a/TGServiceInterface/Components/Instance.cs +++ b/TGServiceInterface/Components/Instance.cs @@ -14,5 +14,12 @@ namespace TGServiceInterface.Components /// The path to the directory on success, null on failure [OperationContract] string ServerDirectory(); + + /// + /// Retrieve's the service's version + /// + /// The service's version + [OperationContract] + string Version(); } } diff --git a/TGServiceInterface/Components/InstanceManager.cs b/TGServiceInterface/Components/InstanceManager.cs new file mode 100644 index 0000000000..3467c009c1 --- /dev/null +++ b/TGServiceInterface/Components/InstanceManager.cs @@ -0,0 +1,62 @@ +using System.ServiceModel; + +namespace TGServiceInterface.Components +{ + /// + /// Used for managing s + /// + [ServiceContract] + public interface ITGInstanceManager + { + /// + /// Creates a new + /// + /// The name of the instance + /// The path to the instance + /// on success, error message on failure + [OperationContract] + string CreateInstance(string Name, string path); + + /// + /// Registers an existing server instance + /// + /// The path to the instance + /// on success, error message on failure + [OperationContract] + string ImportInstance(string path); + + /// + /// Checks if an instance is online + /// + /// The name of the instance + /// if the Instance exists and is online, otherwise + [OperationContract] + bool InstanceEnabled(string Name); + + /// + /// Sets an instance's enabled status + /// + /// The instance whom's status should be changed + /// to enable the instance, to disable it + /// on success, error message on failure + [OperationContract] + string SetInstanceEnabled(string Name, bool enabled); + + /// + /// Renames an instance, this will restart the instance if it is enabled + /// + /// The current name of the instance + /// The new name of the instance + /// on success, error message on failure + [OperationContract] + string RenameInstance(string name, string new_name); + + /// + /// Disables and unregisters an instance, allowing the folder and data to be manipulated manually + /// + /// The instance to detach + /// on success, error message on failure + [OperationContract] + string DetachInstance(string name); + } +} diff --git a/TGServiceInterface/Components/Landing.cs b/TGServiceInterface/Components/Landing.cs new file mode 100644 index 0000000000..a1d18c8c88 --- /dev/null +++ b/TGServiceInterface/Components/Landing.cs @@ -0,0 +1,26 @@ +using System.Collections.Generic; +using System.ServiceModel; + +namespace TGServiceInterface.Components +{ + /// + /// Used for general authentication and listing s + /// + [ServiceContract] + public interface ITGLanding + { + /// + /// Retrieve's the service's version + /// + /// The service's version + [OperationContract] + string Version(); + + /// + /// List instances that the caller can access + /// + /// A of instance names relating to their paths + [OperationContract] + IList ListInstances(); + } +} diff --git a/TGServiceInterface/Components/Service.cs b/TGServiceInterface/Components/Service.cs index e9057d957c..d9fecd397f 100644 --- a/TGServiceInterface/Components/Service.cs +++ b/TGServiceInterface/Components/Service.cs @@ -16,13 +16,6 @@ namespace TGServiceInterface.Components [OperationContract] void PrepareForUpdate(); - /// - /// Retrieve's the service's version - /// - /// The service's version - [OperationContract] - string Version(); - /// /// Get the port used for remote operation /// @@ -40,62 +33,11 @@ namespace TGServiceInterface.Components string SetRemoteAccessPort(ushort port); /// - /// List instances + /// Retrieve's the service's version /// - /// A of instance names relating to their paths + /// The service's version [OperationContract] - IList ListInstances(); - - /// - /// Creates a new server instance - /// - /// The name of the instance - /// The path to the instance - /// on success, error message on failure - [OperationContract] - string CreateInstance(string Name, string path); - - /// - /// Registers an existing server instance - /// - /// The path to the instance - /// on success, error message on failure - [OperationContract] - string ImportInstance(string path); - - /// - /// Checks if an instance is online - /// - /// The name of the instance - /// if the Instance exists and is online, otherwise - [OperationContract] - bool InstanceEnabled(string Name); - - /// - /// Sets an instance's enabled status - /// - /// The instance whom's status should be changed - /// to enable the instance, to disable it - /// on success, error message on failure - [OperationContract] - string SetInstanceEnabled(string Name, bool enabled); - - /// - /// Renames an instance, this will restart the instance if it is enabled - /// - /// The current name of the instance - /// The new name of the instance - /// on success, error message on failure - [OperationContract] - string RenameInstance(string name, string new_name); - - /// - /// Disables and unregisters an instance, allowing the folder and data to be manipulated manually - /// - /// The instance to detach - /// on success, error message on failure - [OperationContract] - string DetachInstance(string name); + string Version(); /// /// Sets the path to the python 2.7 installation diff --git a/TGServiceInterface/Interface.cs b/TGServiceInterface/Interface.cs index 34b9003911..4a3bd34b5b 100644 --- a/TGServiceInterface/Interface.cs +++ b/TGServiceInterface/Interface.cs @@ -52,17 +52,17 @@ namespace TGServiceInterface bool VersionMismatch(out string errorMessage); /// - /// Returns the requested component for the instance . This does not guarantee a successful connection. s created this way are recycled for minimum latency and bandwidth usage + /// Returns the requested component for the instance . This does not guarantee a successful connection. /// /// The component to retrieve /// The correct component T GetComponent(); /// - /// Returns the component for the service + /// Returns a root service component /// /// The component for the service - ITGSService GetService(); + T GetServiceComponent(); /// /// Used to test if the is avaiable on the target machine. Note that state can change at any time and any call into the may throw an exception because of communcation errors @@ -82,9 +82,14 @@ namespace TGServiceInterface sealed public class Interface : IInterface { /// - /// List of s that can be used with and + /// List of s that can be used with /// - public static readonly IList ValidInterfaces = CollectComponents(); + public static readonly IList ValidServiceInterfaces = new List { typeof(ITGSService), typeof(ITGInstanceManager), typeof(ITGConnectivity), typeof(ITGLanding) }; + + /// + /// List of s that can be used with + /// + public static readonly IList ValidInstanceInterfaces = CollectComponents(); /// /// The maximum message size to and from a local server @@ -145,13 +150,13 @@ namespace TGServiceInterface /// A of s that can be used with the service static IList CollectComponents() { - var ServiceComponent = typeof(ITGSService); //this is special - //find all interfaces in this assembly in this namespace that have the service contract attribute + var ConnectivityComponent = typeof(ITGConnectivity); + //find all interfaces in this assembly in this namespace that have the service contract attribute var query = from t in Assembly.GetExecutingAssembly().GetTypes() where t.IsInterface - && t.Namespace == ServiceComponent.Namespace + && t.Namespace == ConnectivityComponent.Namespace && t.GetCustomAttribute(typeof(ServiceContractAttribute)) != null - && t != ServiceComponent + && (t == ConnectivityComponent || !ValidServiceInterfaces.Contains(t)) select t; return query.ToList(); } @@ -288,7 +293,7 @@ namespace TGServiceInterface /// public bool VersionMismatch(out string errorMessage) { - var splits = GetService().Version().Split(' '); + var splits = GetServiceComponent().Version().Split(' '); var theirs = new Version(splits[splits.Length - 1].Substring(1)); var ours = new Version(FileVersionInfo.GetVersionInfo(System.Reflection.Assembly.GetExecutingAssembly().Location).FileVersion); if(theirs.Major != ours.Major || theirs.Minor != ours.Minor || theirs.Revision != ours.Revision) //don't care about the patch level @@ -314,7 +319,7 @@ namespace TGServiceInterface public T GetComponent() { var ToT = typeof(T); - if (!ValidInterfaces.Contains(ToT) && ToT != typeof(ITGSService)) + if (!ValidInstanceInterfaces.Contains(ToT)) throw new Exception("Invalid type!"); return GetComponentImpl(true); } @@ -357,9 +362,12 @@ namespace TGServiceInterface } /// - public ITGSService GetService() + public T GetServiceComponent() { - return GetComponentImpl(false); + var ToT = typeof(T); + if (!ValidServiceInterfaces.Contains(ToT)) + throw new Exception("Invalid type!"); + return GetComponentImpl(false); } /// @@ -420,10 +428,9 @@ namespace TGServiceInterface error = e.ToString(); return ConnectivityLevel.None; } - var service = GetService(); try { - service.Version(); + GetServiceComponent().Version(); } catch(Exception e) { @@ -432,8 +439,7 @@ namespace TGServiceInterface } try { - // TODO - + GetServiceComponent().Version(); error = null; return ConnectivityLevel.Administrator; } diff --git a/TGServiceInterface/TGServiceInterface.csproj b/TGServiceInterface/TGServiceInterface.csproj index 2432434246..1c5769d1ba 100644 --- a/TGServiceInterface/TGServiceInterface.csproj +++ b/TGServiceInterface/TGServiceInterface.csproj @@ -57,6 +57,8 @@ + +