WIP group authentication service side

This commit is contained in:
Cyberboss
2017-09-14 14:38:38 -04:00
parent b790154fdf
commit 035f169d57
8 changed files with 169 additions and 4 deletions
+3
View File
@@ -67,6 +67,9 @@
<setting name="CertificateURL" serializeAs="String">
<value>localhost</value>
</setting>
<setting name="AuthorizedGroupName" serializeAs="String">
<value />
</setting>
</TGServerService.Properties.Settings>
</userSettings>
</configuration>
+91
View File
@@ -0,0 +1,91 @@
using System;
using System.Collections.Generic;
using System.DirectoryServices.AccountManagement;
using System.Security.Principal;
using System.ServiceModel;
using System.ServiceModel.Dispatcher;
using TGServiceInterface;
namespace TGServerService
{
partial class TGStationServer : ServiceAuthorizationManager, ITGAdministration
{
SecurityIdentifier TheDroidsWereLookingFor;
/// <inheritdoc />
public string GetCurrentAuthorizedGroup()
{
try
{
return TheDroidsWereLookingFor != null ? TheDroidsWereLookingFor.Translate(typeof(GroupPrincipal)).ToString() : "ADMIN";
}
catch
{
return null;
}
}
/// <inheritdoc />
public string SetAuthorizedGroup(string groupName)
{
if(groupName == null)
{
TheDroidsWereLookingFor = null;
var config = Properties.Settings.Default;
config.AuthorizedGroupName = 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.AuthorizedGroupName;
if (String.IsNullOrWhiteSpace(groupName))
return null;
var gp = GroupPrincipal.FindByIdentity(pc, search != null ? IdentityType.Name : IdentityType.Sid, groupName);
if (gp == null)
return null;
TheDroidsWereLookingFor = gp.Sid;
if (search != null)
{
config.AuthorizedGroupName = TheDroidsWereLookingFor.Value;
config.Save();
}
return gp.Name;
}
protected override bool CheckAccessCore(OperationContext operationContext)
{
if (operationContext.EndpointDispatcher.ContractName == typeof(ITGConnectivity).Name) //always allow connectivity checks
return true;
var windowsIdent = operationContext.ServiceSecurityContext.WindowsIdentity;
//first allow admins
var authSuccess = new WindowsPrincipal(windowsIdent).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)
//and allow those in the authorized group
authSuccess = (TheDroidsWereLookingFor != null || windowsIdent.Groups.Contains(TheDroidsWereLookingFor));
var actions = new List<string>();
try
{
var realfilter = (ActionMessageFilter)operationContext.EndpointDispatcher.ContractFilter;
var cnamespace = operationContext.EndpointDispatcher.ContractNamespace;
foreach (var I in realfilter.Actions)
actions.Add(I.Replace(cnamespace, "")); //filter out some garbage
}
catch (Exception e)
{
TGServerService.WriteError("IF YOU SEE THIS CALL CYBERBOSS: " + e.ToString(), TGServerService.EventID.Authentication);
}
TGServerService.WriteAccess(operationContext.ServiceSecurityContext.WindowsIdentity.Name, actions, authSuccess);
return authSuccess;
}
}
}
+1
View File
@@ -16,6 +16,7 @@ namespace TGServerService
//called when the service is started
public TGStationServer()
{
FindTheDroidsWereLookingFor();
InitChat();
InitByond();
InitCompiler();
+12
View File
@@ -250,5 +250,17 @@ namespace TGServerService.Properties {
this["CertificateURL"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("")]
public string AuthorizedGroupName {
get {
return ((string)(this["AuthorizedGroupName"]));
}
set {
this["AuthorizedGroupName"] = value;
}
}
}
}
@@ -59,5 +59,8 @@
<Setting Name="CertificateURL" Type="System.String" Scope="User">
<Value Profile="(Default)">localhost</Value>
</Setting>
<Setting Name="AuthorizedGroupName" Type="System.String" Scope="User">
<Value Profile="(Default)" />
</Setting>
</Settings>
</SettingsFile>
+12 -3
View File
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Security.Cryptography.X509Certificates;
@@ -13,6 +14,7 @@ namespace TGServerService
//only deprecate events, do not reuse them
public enum EventID
{
Authentication = 1,
ChatCommand = 100,
ChatConnectFail = 200,
ChatProviderStartFail = 300,
@@ -95,6 +97,11 @@ namespace TGServerService
ActiveService.EventLog.WriteEntry(message, EventLogEntryType.Warning, (int)id);
}
public static void WriteAccess(string username, IList<string> actions, bool authSuccess)
{
ActiveService.EventLog.WriteEntry(String.Format("Access from: {0}. Actions: {1}", username, String.Join(", ", actions)), authSuccess ? EventLogEntryType.SuccessAudit : EventLogEntryType.FailureAudit, (int)EventID.Authentication);
}
ServiceHost host; //the WCF host
void MigrateSettings(int oldVersion, int newVersion)
@@ -142,16 +149,18 @@ namespace TGServerService
}
Environment.CurrentDirectory = Config.ServerDirectory;
host = new ServiceHost(typeof(TGStationServer), new Uri[] { new Uri("net.pipe://localhost"), new Uri(String.Format("https://localhost:{0}", Server.HTTPSPort)) })
var instance = new TGStationServer();
host = new ServiceHost(instance, new Uri[] { new Uri("net.pipe://localhost"), new Uri(String.Format("https://localhost:{0}", Server.HTTPSPort)) })
{
CloseTimeout = new TimeSpan(0, 0, 5)
}; //construction runs here
};
foreach (var I in Server.ValidInterfaces)
AddEndpoint(I);
host.Credentials.ServiceCertificate.SetCertificate(StoreLocation.LocalMachine, StoreName.My, X509FindType.FindBySubjectName, Config.CertificateURL);
host.Credentials.UserNameAuthentication.IncludeWindowsGroups = true;
host.Authorization.ServiceAuthorizationManager = instance;
host.Open(); //...or maybe here, doesn't really matter
}
+2
View File
@@ -67,6 +67,7 @@
<Reference Include="System" />
<Reference Include="System.ComponentModel.Composition" />
<Reference Include="System.Configuration.Install" />
<Reference Include="System.DirectoryServices.AccountManagement" />
<Reference Include="System.IO.Compression" />
<Reference Include="System.IO.Compression.FileSystem" />
<Reference Include="System.Numerics" />
@@ -83,6 +84,7 @@
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="AuthorizationManager.cs" />
<Compile Include="Byond.cs" />
<Compile Include="Chat.cs" />
<Compile Include="ChatCommands.cs" />
+45 -1
View File
@@ -9,7 +9,7 @@ namespace TGServiceInterface
/// <summary>
/// List of types that can be used with GetComponen
/// </summary>
public static readonly IList<Type> ValidInterfaces = new List<Type> { typeof(ITGByond), typeof(ITGChat), typeof(ITGCompiler), typeof(ITGConfig), typeof(ITGDreamDaemon), typeof(ITGRepository), typeof(ITGSService), typeof(ITGConnectivity) };
public static readonly IList<Type> ValidInterfaces = new List<Type> { typeof(ITGByond), typeof(ITGChat), typeof(ITGCompiler), typeof(ITGConfig), typeof(ITGDreamDaemon), typeof(ITGRepository), typeof(ITGSService), typeof(ITGConnectivity), typeof(ITGAdministration) };
/// <summary>
/// Base name of the communication pipe
@@ -111,6 +111,7 @@ namespace TGServiceInterface
/// <summary>
/// As opposed to VerifyConnection(), this check user credentials
/// Requires a prior call to <see cref="VerifyConnection"/>
/// </summary>
/// <returns>true if credentials are valid, false otherwise</returns>
public static bool Authenticate()
@@ -125,8 +126,29 @@ namespace TGServiceInterface
return false;
}
}
/// <summary>
/// As opposed to Authentication() this returns true if the current login can use the <see cref="ITGAdministration"/> interface.
/// Requires a prior call to <see cref="Authenticate"/>
/// </summary>
/// <returns>true if the connection may use the <see cref="ITGAdministration"/> interface, false otherwise</returns>
public static bool AuthenticateAdmin()
{
try
{
GetComponent<ITGAdministration>().GetCurrentAuthorizedGroup();
return true;
}
catch
{
return false;
}
}
}
/// <summary>
/// Used for testing connections to the service without authentication
/// </summary>
[ServiceContract]
public interface ITGConnectivity
{
@@ -137,6 +159,28 @@ namespace TGServiceInterface
void VerifyConnection();
}
/// <summary>
/// Manage the group that is used to access the service, can only be used by an administrator
/// </summary>
[ServiceContract]
public interface ITGAdministration
{
/// <summary>
/// Returns the name of the windows group allowed to use the service other than administrator
/// </summary>
/// <returns>The name of the windows group allowed to use the service other than administrator, "ADMIN" if it's unset, null on failure</returns>
[OperationContract]
string GetCurrentAuthorizedGroup();
/// <summary>
/// Searches the windows machine for the group named <paramref name="groupName"/>, sets it as the authorized group if it's found
/// </summary>
/// <param name="groupName">The name of the group to search for or null to clear the setting</param>
/// <returns>The full name of the group that is now authorized on success, null on failure, "ADMIN" on clearing</returns>
[OperationContract]
string SetAuthorizedGroup(string groupName);
}
/// <summary>
/// Interface for managing the service
/// </summary>