using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Xml;
namespace Webserver {
public class WebPermissions {
private const string permissionsFileName = "webpermissions.xml";
private static WebPermissions instance;
private readonly FileSystemWatcher fileWatcher;
private readonly WebModulePermission defaultModulePermission = new WebModulePermission ("", 0);
///
/// Registered user/pass admin tokens
///
private readonly Dictionary adminTokens = new CaseInsensitiveStringDictionary ();
///
/// Contains all registered modules and their default permission
///
private readonly Dictionary knownModules = new CaseInsensitiveStringDictionary ();
///
/// Manually defined module permissions
///
private readonly Dictionary modulePermissions =
new CaseInsensitiveStringDictionary ();
///
/// Public list of all modules, both those with custom permissions as well as those that do not with their default permission
///
private readonly List allModulesList = new List ();
private readonly ReadOnlyCollection allModulesListRo;
private static string SettingsFilePath => GamePrefs.GetString (EnumUtils.Parse ("SaveGameFolder"));
private static string SettingsFileName => permissionsFileName;
private static string SettingsFullPath => SettingsFilePath + "/" + SettingsFileName;
private WebPermissions () {
allModulesListRo = new ReadOnlyCollection (allModulesList);
Directory.CreateDirectory (SettingsFilePath);
Load ();
fileWatcher = new FileSystemWatcher (SettingsFilePath, SettingsFileName);
fileWatcher.Changed += OnFileChanged;
fileWatcher.Created += OnFileChanged;
fileWatcher.Deleted += OnFileChanged;
fileWatcher.EnableRaisingEvents = true;
}
public static WebPermissions Instance {
get {
lock (typeof (WebPermissions)) {
return instance ??= new WebPermissions ();
}
}
}
#region Admin Tokens
public void AddAdmin (string _name, string _token, int _permissionLevel, bool _save = true) {
AdminToken c = new AdminToken (_name, _token, _permissionLevel);
lock (this) {
adminTokens [_name] = c;
if (_save) {
Save ();
}
}
}
public void RemoveAdmin (string _name, bool _save = true) {
lock (this) {
adminTokens.Remove (_name);
if (_save) {
Save ();
}
}
}
public bool IsAdmin (string _name) {
return adminTokens.ContainsKey (_name);
}
public AdminToken[] GetAdmins () {
AdminToken[] result = new AdminToken[adminTokens.Count];
adminTokens.CopyValuesTo (result);
return result;
}
public AdminToken GetWebAdmin (string _name, string _token) {
if (IsAdmin (_name) && adminTokens [_name].token == _token) {
return adminTokens [_name];
}
return null;
}
#endregion
#region Modules
public void AddModulePermission (string _module, int _permissionLevel, bool _save = true) {
WebModulePermission p = new WebModulePermission (_module, _permissionLevel);
lock (this) {
allModulesList.Clear ();
modulePermissions [_module] = p;
if (_save) {
Save ();
}
}
}
public void AddKnownModule (string _module, int _defaultPermission) {
if (string.IsNullOrEmpty (_module)) {
return;
}
WebModulePermission p = new WebModulePermission (_module, _defaultPermission);
lock (this) {
allModulesList.Clear ();
knownModules [_module] = p;
}
}
public bool IsKnownModule (string _module) {
if (string.IsNullOrEmpty (_module)) {
return false;
}
lock (this) {
return knownModules.ContainsKey (_module);
}
}
public void RemoveModulePermission (string _module, bool _save = true) {
lock (this) {
allModulesList.Clear ();
modulePermissions.Remove (_module);
if (_save) {
Save ();
}
}
}
public IList GetModules () {
if (allModulesList.Count != 0) {
return allModulesListRo;
}
foreach ((string moduleName, WebModulePermission moduleDefaultPerm) in knownModules) {
allModulesList.Add (modulePermissions.TryGetValue (moduleName, out WebModulePermission modulePermission)
? modulePermission
: moduleDefaultPerm);
}
return allModulesListRo;
}
public bool ModuleAllowedWithLevel (string _module, int _level) {
WebModulePermission permInfo = GetModulePermission (_module);
return permInfo.permissionLevel >= _level;
}
public WebModulePermission GetModulePermission (string _module) {
if (modulePermissions.TryGetValue (_module, out WebModulePermission result)) {
return result;
}
return knownModules.TryGetValue (_module, out result) ? result : defaultModulePermission;
}
#endregion
#region IO Tasks
private void OnFileChanged (object _source, FileSystemEventArgs _e) {
Log.Out ("[Web] [Perms] Reloading " + SettingsFileName);
Load ();
}
public void Load () {
adminTokens.Clear ();
modulePermissions.Clear ();
if (!File.Exists (SettingsFullPath)) {
Log.Out ($"[Web] [Perms] Permissions file '{SettingsFileName}' not found, creating.");
Save ();
return;
}
Log.Out ($"[Web] [Perms] Loading permissions file at '{SettingsFullPath}'");
XmlDocument xmlDoc = new XmlDocument ();
try {
xmlDoc.Load (SettingsFullPath);
} catch (XmlException e) {
Log.Error ("[Web] [Perms] Failed loading permissions file: " + e.Message);
return;
}
XmlNode adminToolsNode = xmlDoc.DocumentElement;
if (adminToolsNode == null) {
Log.Error ("[Web] [Perms] Failed loading permissions file: No DocumentElement found");
return;
}
foreach (XmlNode childNode in adminToolsNode.ChildNodes) {
switch (childNode.Name) {
case "admintokens":
ParseAdminTokens (childNode);
break;
case "permissions":
ParseModulePermissions (childNode);
break;
}
}
Log.Out ("[Web] [Perms] Loading permissions file done.");
}
private void ParseAdminTokens (XmlNode _baseNode) {
foreach (XmlNode subChild in _baseNode.ChildNodes) {
if (subChild.NodeType == XmlNodeType.Comment) {
continue;
}
if (subChild.NodeType != XmlNodeType.Element) {
Log.Warning ($"[Web] [Perms] Unexpected XML node found in 'admintokens' section: {subChild.OuterXml}");
continue;
}
XmlElement lineItem = (XmlElement)subChild;
if (!lineItem.HasAttribute ("name")) {
Log.Warning ($"[Web] [Perms] Ignoring admintoken-entry because of missing 'name' attribute: {subChild.OuterXml}");
continue;
}
if (!lineItem.HasAttribute ("token")) {
Log.Warning ($"[Web] [Perms] Ignoring admintoken-entry because of missing 'token' attribute: {subChild.OuterXml}");
continue;
}
if (!lineItem.HasAttribute ("permission_level")) {
Log.Warning ($"[Web] [Perms] Ignoring admintoken-entry because of missing 'permission_level' attribute: {subChild.OuterXml}");
continue;
}
string name = lineItem.GetAttribute ("name");
string token = lineItem.GetAttribute ("token");
if (!int.TryParse (lineItem.GetAttribute ("permission_level"), out int permissionLevel)) {
Log.Warning (
$"[Web] [Perms] Ignoring admintoken-entry because of invalid (non-numeric) value for 'permission_level' attribute: {subChild.OuterXml}");
continue;
}
AddAdmin (name, token, permissionLevel, false);
}
}
private void ParseModulePermissions (XmlNode _baseNode) {
foreach (XmlNode subChild in _baseNode.ChildNodes) {
if (subChild.NodeType == XmlNodeType.Comment) {
continue;
}
if (subChild.NodeType != XmlNodeType.Element) {
Log.Warning ($"[Web] [Perms] Unexpected XML node found in 'permissions' section: {subChild.OuterXml}");
continue;
}
XmlElement lineItem = (XmlElement)subChild;
if (!lineItem.HasAttribute ("module")) {
Log.Warning ($"[Web] [Perms] Ignoring permission-entry because of missing 'module' attribute: {subChild.OuterXml}");
continue;
}
if (!lineItem.HasAttribute ("permission_level")) {
Log.Warning ($"[Web] [Perms] Ignoring permission-entry because of missing 'permission_level' attribute: {subChild.OuterXml}");
continue;
}
if (!int.TryParse (lineItem.GetAttribute ("permission_level"), out int permissionLevel)) {
Log.Warning (
$"[Web] [Perms] Ignoring permission-entry because of invalid (non-numeric) value for 'permission_level' attribute: {subChild.OuterXml}");
continue;
}
AddModulePermission (lineItem.GetAttribute ("module"), permissionLevel, false);
}
}
public void Save () {
XmlDocument xml = new XmlDocument ();
xml.CreateXmlDeclaration ();
// xml.AddXmlComment (XmlHeader);
XmlElement root = xml.AddXmlElement ("webpermissions");
// AdminTokens
XmlElement adminTokensElem = root.AddXmlElement ("admintokens");
adminTokensElem.AddXmlComment (" ");
foreach ((string _, AdminToken adminToken) in adminTokens) {
adminTokensElem.AddXmlElement ("token")
.SetAttrib ("name", adminToken.name)
.SetAttrib ("token", adminToken.token)
.SetAttrib ("permission_level", adminToken.permissionLevel.ToString ());
}
XmlElement modulePermissionsElem = root.AddXmlElement ("permissions");
foreach ((string _, WebModulePermission webModulePermission) in modulePermissions) {
modulePermissionsElem.AddXmlElement ("permission")
.SetAttrib ("module", webModulePermission.module)
.SetAttrib ("permission_level", webModulePermission.permissionLevel.ToString ());
}
fileWatcher.EnableRaisingEvents = false;
xml.Save (SettingsFullPath);
fileWatcher.EnableRaisingEvents = true;
}
#endregion
public class AdminToken {
public readonly string name;
public readonly int permissionLevel;
public readonly string token;
public AdminToken (string _name, string _token, int _permissionLevel) {
name = _name;
token = _token;
permissionLevel = _permissionLevel;
}
}
public struct WebModulePermission {
public readonly string module;
public readonly int permissionLevel;
public WebModulePermission (string _module, int _permissionLevel) {
module = _module;
permissionLevel = _permissionLevel;
}
}
}
}