mirror of
https://github.com/tgstation/tgstation-server.git
synced 2026-08-29 16:11:05 +01:00
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:
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user