Skip to content

Commit

Permalink
(#996) Create config directory if it doesn't exist
Browse files Browse the repository at this point in the history
If the directory for a LiteDatabase doesn't exist, LiteDatabase will not
attempt to create it, and will throw an exception because it can't
create the file without the parent directory. This ensures that the
folder exists before trying to create the database.
  • Loading branch information
corbob committed Apr 24, 2024
1 parent 86e6f63 commit 600b9f8
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 6 deletions.
15 changes: 12 additions & 3 deletions Source/ChocolateyGui.Common.Windows/Startup/ChocolateyGuiModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -148,13 +148,22 @@ protected override void Load(ContainerBuilder builder)
var userDatabase = new LiteDatabase($"filename={Path.Combine(Bootstrapper.LocalAppDataPath, "data.db")};upgrade=true");

LiteDatabase globalDatabase;
var globalConfigDirectory = Path.Combine(Bootstrapper.AppDataPath, "Config");
var globalConfigDatabaseFile = Path.Combine(globalConfigDirectory, "data.db");

if (Hacks.IsElevated)
{
globalDatabase = new LiteDatabase($"filename={Path.Combine(Bootstrapper.AppDataPath, "Config", "data.db")};upgrade=true");
if (!Directory.Exists(globalConfigDirectory))
{
Directory.CreateDirectory(globalConfigDirectory);
Hacks.LockDirectory(globalConfigDirectory);
}

globalDatabase = new LiteDatabase($"filename={globalConfigDatabaseFile};upgrade=true");
}
else
{
if (!File.Exists(Path.Combine(Bootstrapper.AppDataPath, "Config", "data.db")))
if (!File.Exists(globalConfigDatabaseFile))
{
// Since the global configuration database file doesn't exist, we must be running in a state where an administrator user
// has never run Chocolatey GUI. In this case, use null, which will mean attempts to use the global database will be ignored.
Expand All @@ -163,7 +172,7 @@ protected override void Load(ContainerBuilder builder)
else
{
// Since this is a non-administrator user, they should only have read permissions to this database
globalDatabase = new LiteDatabase($"filename={Path.Combine(Bootstrapper.AppDataPath, "Config", "data.db")};readonly=true");
globalDatabase = new LiteDatabase($"filename={globalConfigDatabaseFile};readonly=true");
}
}

Expand Down
31 changes: 31 additions & 0 deletions Source/ChocolateyGui.Common/Hacks.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,43 @@
// </copyright>
// --------------------------------------------------------------------------------------------------------------------

using System;
using System.IO;
using System.Security.AccessControl;
using System.Security.Principal;

namespace ChocolateyGui.Common
{
public static class Hacks
{
public static bool IsElevated => (WindowsIdentity.GetCurrent().Owner?.IsWellKnown(WellKnownSidType.BuiltinAdministratorsSid)).GetValueOrDefault(false);

// TODO: Replace this LockDirectory with calls to DotNetFileSystem's LockDirectory when https://github.com/chocolatey/ChocolateyGUI/issues/1046 is completed.
/// <summary>
/// Lock the given directory path to just Administrators being able to write. This method is copied from chocolatey.infrastructure.filesystem.DotNetFileSystem, and should be replaced with that call when the minimum Chocolatey.lib is bumped to 2.2.0 or greater.
/// </summary>
/// <param name="directoryPath">Directory path to lock down.</param>
public static void LockDirectory(string directoryPath)
{
var permissions = Directory.GetAccessControl(directoryPath);
var rules = permissions.GetAccessRules(includeExplicit: true, includeInherited: true, targetType: typeof(NTAccount));

// We first need to remove all rules
foreach (FileSystemAccessRule rule in rules)
{
permissions.RemoveAccessRuleAll(rule);
}

var bultinAdmins = new SecurityIdentifier(WellKnownSidType.BuiltinAdministratorsSid, null).Translate(typeof(NTAccount));
var localsystem = new SecurityIdentifier(WellKnownSidType.LocalSystemSid, null).Translate(typeof(NTAccount));
var builtinUsers = new SecurityIdentifier(WellKnownSidType.BuiltinUsersSid, null).Translate(typeof(NTAccount));
var inheritanceFlags = InheritanceFlags.ContainerInherit | InheritanceFlags.ObjectInherit;
permissions.SetAccessRule(new FileSystemAccessRule(bultinAdmins, FileSystemRights.FullControl, inheritanceFlags, PropagationFlags.None, AccessControlType.Allow));
permissions.SetAccessRule(new FileSystemAccessRule(localsystem, FileSystemRights.FullControl, inheritanceFlags, PropagationFlags.None, AccessControlType.Allow));
permissions.SetAccessRule(new FileSystemAccessRule(builtinUsers, FileSystemRights.ReadAndExecute, inheritanceFlags, PropagationFlags.None, AccessControlType.Allow));
permissions.SetOwner(bultinAdmins);
permissions.SetAccessRuleProtection(isProtected: true, preserveInheritance: false);
Directory.SetAccessControl(directoryPath, permissions);
}
}
}
15 changes: 12 additions & 3 deletions Source/ChocolateyGuiCli/Startup/ChocolateyGuiCliModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,22 @@ protected override void Load(ContainerBuilder builder)
var userDatabase = new LiteDatabase($"filename={Path.Combine(Bootstrapper.LocalAppDataPath, "data.db")};upgrade=true");

LiteDatabase globalDatabase;
var globalConfigDirectory = Path.Combine(Bootstrapper.AppDataPath, "Config");
var globalConfigDatabaseFile = Path.Combine(globalConfigDirectory, "data.db");

if (Hacks.IsElevated)
{
globalDatabase = new LiteDatabase($"filename={Path.Combine(Bootstrapper.AppDataPath, "Config", "data.db")};upgrade=true");
if (!Directory.Exists(globalConfigDirectory))
{
Directory.CreateDirectory(globalConfigDirectory);
Hacks.LockDirectory(globalConfigDirectory);
}

globalDatabase = new LiteDatabase($"filename={globalConfigDatabaseFile};upgrade=true");
}
else
{
if (!File.Exists(Path.Combine(Bootstrapper.AppDataPath, "Config", "data.db")))
if (!File.Exists(globalConfigDatabaseFile))
{
// Since the global configuration database file doesn't exist, we must be running in a state where an administrator user
// has never run Chocolatey GUI. In this case, use null, which will mean attempts to use the global database will be ignored.
Expand All @@ -63,7 +72,7 @@ protected override void Load(ContainerBuilder builder)
else
{
// Since this is a non-administrator user, they should only have read permissions to this database
globalDatabase = new LiteDatabase($"filename={Path.Combine(Bootstrapper.AppDataPath, "Config", "data.db")};readonly=true");
globalDatabase = new LiteDatabase($"filename={globalConfigDatabaseFile};readonly=true");
}
}

Expand Down

0 comments on commit 600b9f8

Please sign in to comment.