Massive sanity reorganization of project structure

Overall:
- Removed all 'TG' prefixes on classes and enums
Service:
- TGStationServer => ServerInstance
- TGServerService => Service
- Moved all ServerInstance partial classes to a subfolder
- Moved ChatProvider classes, delegate, and interface to a namespace
- Moved EventID and ChatMessageType to their own .cs files
- Moved SanitizeTopicString to Program
- ServerService.cs => Service.cs
- Fixed ProjectInstaller being in it's own namespace instead of
TGServerService
Interface:
- Moved ExitCode enum to Command class
- Moved all components to a namespace
- Moved all component enums to Enumerations.cs
- DDInteropCallHolder => DreamDaemonBridge
- Move PullRequestInfo and DreamDaemonBridge to their own .cs files
- Changes the type of ChannelFactory cache from Dictionary<Type,
ChannelFactory> to IDictionary<Type, ChannelFactory>
This commit is contained in:
Cyberboss
2017-10-20 14:00:09 -04:00
parent 7b8fa03f5d
commit 3137be7abb
58 changed files with 3274 additions and 3212 deletions
@@ -0,0 +1,283 @@
using System;
using System.DirectoryServices.AccountManagement;
using System.IO;
using System.Security.Principal;
using System.ServiceModel;
using System.Threading;
using TGServiceInterface;
using TGServiceInterface.Components;
namespace TGServerService
{
//note this only works with MACHINE LOCAL groups and admins for now
//if someone wants AD shit, code it yourself
partial class ServerInstance : ServiceAuthorizationManager, ITGAdministration
{
SecurityIdentifier TheDroidsWereLookingFor;
object authLock = new object();
string LastSeenUser = null;
readonly SecurityIdentifier ServiceSID = WindowsIdentity.GetCurrent().User;
/// <inheritdoc />
public string GetCurrentAuthorizedGroup()
{
try
{
if (TheDroidsWereLookingFor == null)
return "ADMIN";
var pc = new PrincipalContext(ContextType.Machine);
return GroupPrincipal.FindByIdentity(pc, IdentityType.Sid, TheDroidsWereLookingFor.Value).Name;
}
catch
{
return null;
}
}
/// <inheritdoc />
public string SetAuthorizedGroup(string groupName)
{
if (groupName == null)
{
TheDroidsWereLookingFor = null;
var config = Properties.Settings.Default;
config.AuthorizedGroupSID = null;
config.Save();
return "ADMIN";
}
return FindTheDroidsWereLookingFor(groupName);
}
string FindTheDroidsWereLookingFor(string search = null)
{
//find the group that is authorized to use the tools
var pc = new PrincipalContext(ContextType.Machine);
var config = Properties.Settings.Default;
var groupName = search ?? config.AuthorizedGroupSID;
if (String.IsNullOrWhiteSpace(groupName))
return null;
var gp = GroupPrincipal.FindByIdentity(pc, search != null ? IdentityType.Name : IdentityType.Sid, groupName);
if (gp == null)
{
if (search != null)
//try again with all types
gp = GroupPrincipal.FindByIdentity(pc, search);
if (gp == null)
return null;
}
TheDroidsWereLookingFor = gp.Sid;
if (search != null)
{
config.AuthorizedGroupSID = TheDroidsWereLookingFor.Value;
config.Save();
}
return gp.Name;
}
//This function checks for authorization whenever an API call is made
//This does NOT validate the windows account, that is done when the user connects internally
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;
if (contract == typeof(ITGInterop).Name) //only allow the same user the service is running as to use interop, because that's what DD is running as
return windowsIdent.User == ServiceSID;
var wp = new WindowsPrincipal(windowsIdent);
//first allow admins
var authSuccess = wp.IsInRole(WindowsBuiltInRole.Administrator);
//if we're not an admin, check that we aren't trying to access the admin interface
if (!authSuccess && operationContext.EndpointDispatcher.ContractName != typeof(ITGAdministration).Name && TheDroidsWereLookingFor != null)
authSuccess = wp.IsInRole(new SecurityIdentifier(Properties.Settings.Default.AuthorizedGroupSID));
lock (authLock)
{
var user = windowsIdent.Name;
if (LastSeenUser != user)
{
LastSeenUser = user;
Service.WriteAccess(user, authSuccess);
}
}
return authSuccess;
}
/// <inheritdoc />
public ushort RemoteAccessPort()
{
return Properties.Settings.Default.RemoteAccessPort;
}
/// <inheritdoc />
public string SetRemoteAccessPort(ushort port)
{
if (port == 0)
return "Cannot bind to port 0";
var Config = Properties.Settings.Default;
Config.RemoteAccessPort = port;
Config.Save();
return null;
}
/// <inheritdoc />
public string MoveServer(string new_location)
{
var Config = Properties.Settings.Default;
try
{
var di1 = new DirectoryInfo(Config.ServerDirectory);
var di2 = new DirectoryInfo(new_location);
var copy = di1.Root.FullName != di2.Root.FullName;
if (copy && File.Exists(PrivateKeyPath))
return String.Format("Unable to perform a cross drive server move with the {0}. Copy aborted!", PrivateKeyPath);
new_location = di2.FullName;
while (di2.Parent != null)
if (di2.Parent.FullName == di1.FullName)
return "Cannot move to child of current directory!";
else
di2 = di2.Parent;
if (!Monitor.TryEnter(RepoLock))
return "Repo locked!";
try
{
if (RepoBusy)
return "Repo busy!";
DisposeRepo();
if (!Monitor.TryEnter(ByondLock))
return "BYOND locked";
try
{
if (updateStat != ByondStatus.Idle)
return "BYOND busy!";
if (!Monitor.TryEnter(CompilerLock))
return "Compiler locked!";
try
{
if (compilerCurrentStatus != CompilerStatus.Uninitialized && compilerCurrentStatus != CompilerStatus.Initialized)
return "Compiler busy!";
if (!Monitor.TryEnter(watchdogLock))
return "Watchdog locked!";
try
{
if (currentStatus != DreamDaemonStatus.Offline)
return "Watchdog running!";
lock (configLock)
{
CleanGameFolder();
Program.DeleteDirectory(GameDir);
string error = null;
if (copy)
{
Program.CopyDirectory(Config.ServerDirectory, new_location);
Directory.CreateDirectory(new_location);
Environment.CurrentDirectory = new_location;
try
{
Program.DeleteDirectory(Config.ServerDirectory);
}
catch (Exception e)
{
error = "The move was successful, but the path " + Config.ServerDirectory + " was unable to be deleted fully!";
Service.WriteWarning(String.Format("Server move from {0} to {1} partial success: {2}", Config.ServerDirectory, new_location, e.ToString()), EventID.ServerMovePartial);
}
}
else
{
try
{
Environment.CurrentDirectory = di2.Root.FullName;
Directory.Move(Config.ServerDirectory, new_location);
Environment.CurrentDirectory = new_location;
}
catch
{
Environment.CurrentDirectory = Config.ServerDirectory;
throw;
}
}
Service.WriteInfo(String.Format("Server moved from {0} to {1}", Config.ServerDirectory, new_location), EventID.ServerMoveComplete);
Config.ServerDirectory = new_location;
return null;
}
}
finally
{
Monitor.Exit(watchdogLock);
}
}
finally
{
Monitor.Exit(CompilerLock);
}
}
finally
{
Monitor.Exit(ByondLock);
}
}
finally
{
Monitor.Exit(RepoLock);
}
}
catch (Exception e)
{
Service.WriteError(String.Format("Server move from {0} to {1} failed: {2}", Config.ServerDirectory, new_location, e.ToString()), EventID.ServerMoveFailed);
return e.ToString();
}
}
public string RecreateStaticFolder()
{
if (!Monitor.TryEnter(RepoLock))
return "Repo locked!";
try
{
if (!Monitor.TryEnter(watchdogLock))
return "Watchdog locked!";
try
{
if (!Monitor.TryEnter(configLock))
return "Static dir locked!";
try
{
if (currentStatus != DreamDaemonStatus.Offline)
return "Watchdog running!";
BackupAndDeleteStaticDirectory();
InitialConfigureRepository();
}
finally
{
Monitor.Exit(configLock);
}
}
finally
{
Monitor.Exit(watchdogLock);
}
}
catch(Exception e)
{
return e.ToString();
}
finally
{
Monitor.Exit(RepoLock);
}
return null;
}
}
}