Skip to content

Commit

Permalink
(#829) Ensure application starts without global db
Browse files Browse the repository at this point in the history
There is a situation when the global configuration database doesn't
exist, for example, when Chocolatey GUI is installed by administrator
but never executed by that user. When running as a non-administrrator
user, the application would then crash.  This changes makes it possible
for the application to simply ignore the fact that the global database
doesn't exist, and instead, simply use the default settings, with no
attempt to then write any values back to the database.

In order to work correctly, there is an update required in the Chocolatey
GUI Licensed extension to make things work there as well.
  • Loading branch information
gep13 committed Mar 29, 2021
1 parent 878db54 commit b02b4dc
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 14 deletions.
30 changes: 24 additions & 6 deletions Source/ChocolateyGui.Common.Windows/Startup/ChocolateyGuiModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -111,11 +111,30 @@ protected override void Load(ContainerBuilder builder)
{
var userDatabase = new LiteDatabase($"filename={Path.Combine(Bootstrapper.LocalAppDataPath, "data.db")};upgrade=true");

LiteDatabase globalDatabase = null;

globalDatabase = Hacks.IsElevated
? new LiteDatabase($"filename={Path.Combine(Bootstrapper.AppDataPath, "Config", "data.db")};upgrade=true")
: new LiteDatabase($"filename={Path.Combine(Bootstrapper.AppDataPath, "Config", "data.db")};upgrade=true;readonly=true");
LiteDatabase globalDatabase;
if (Hacks.IsElevated)
{
globalDatabase = new LiteDatabase($"filename={Path.Combine(Bootstrapper.AppDataPath, "Config", "data.db")};upgrade=true");
}
else
{
if (!File.Exists(Path.Combine(Bootstrapper.AppDataPath, "Config", "data.db")))
{
// 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.
globalDatabase = null;
}
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");
}
}

if (globalDatabase != null)
{
builder.RegisterInstance(globalDatabase).As<LiteDatabase>().SingleInstance().Named<LiteDatabase>(Bootstrapper.GlobalConfigurationDatabaseName);
}

var configService = new ConfigService(globalDatabase, userDatabase);
configService.SetEffectiveConfiguration();
Expand All @@ -128,7 +147,6 @@ protected override void Load(ContainerBuilder builder)

// Since there are two instances of LiteDB, they are added as named instances, so that they can be retrieved when required
builder.RegisterInstance(userDatabase).As<LiteDatabase>().SingleInstance().Named<LiteDatabase>(Bootstrapper.UserConfigurationDatabaseName);
builder.RegisterInstance(globalDatabase).As<LiteDatabase>().SingleInstance().Named<LiteDatabase>(Bootstrapper.GlobalConfigurationDatabaseName);
}
catch (IOException ex)
{
Expand Down
26 changes: 22 additions & 4 deletions Source/ChocolateyGui.Common/Services/ConfigService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,6 @@ public class ConfigService : IConfigService
{
public ConfigService(LiteDatabase globalDatabase, LiteDatabase userDatabase)
{
GlobalCollection = globalDatabase.GetCollection<AppConfiguration>(nameof(AppConfiguration));
UserCollection = userDatabase.GetCollection<AppConfiguration>(nameof(AppConfiguration));

var defaultGlobalSettings = new AppConfiguration()
{
Id = "v0.18.0",
Expand All @@ -39,7 +36,20 @@ public ConfigService(LiteDatabase globalDatabase, LiteDatabase userDatabase)
Id = "v0.18.0"
};

GlobalAppConfiguration = GlobalCollection.FindById("v0.18.0") ?? defaultGlobalSettings;
// If the global database is null, the assumption has to be that we are running as a non-administrator
// user, as such, we should proceed with default settings
if (globalDatabase == null)
{
GlobalCollection = null;
GlobalAppConfiguration = defaultGlobalSettings;
}
else
{
GlobalCollection = globalDatabase.GetCollection<AppConfiguration>(nameof(AppConfiguration));
GlobalAppConfiguration = GlobalCollection.FindById("v0.18.0") ?? defaultGlobalSettings;
}

UserCollection = userDatabase.GetCollection<AppConfiguration>(nameof(AppConfiguration));
UserAppConfiguration = UserCollection.FindById("v0.18.0") ?? defaultUserSettings;
}

Expand Down Expand Up @@ -149,6 +159,14 @@ public AppConfiguration GetGlobalConfiguration()

public void UpdateSettings(AppConfiguration settings, bool global)
{
if (global && GlobalCollection == null)
{
// This is very much an edge case, and we shouldn't ever get to here, but it does need to be handled
Logger.Warning("An attempt has been made to save a configuration change globally, when the global configuration database hasn't been created.");
Logger.Warning("No action will be taken, please check with your System Administrator.");
return;
}

var settingsCollection = global ? GlobalCollection : UserCollection;

if (settingsCollection.Exists(Query.EQ("_id", "v0.18.0")))
Expand Down
28 changes: 24 additions & 4 deletions Source/ChocolateyGuiCli/Startup/ChocolateyGuiCliModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,30 @@ protected override void Load(ContainerBuilder builder)
{
var userDatabase = new LiteDatabase($"filename={Path.Combine(Bootstrapper.LocalAppDataPath, "data.db")};upgrade=true");

var globalDatabase = Hacks.IsElevated
? new LiteDatabase($"filename={Path.Combine(Bootstrapper.AppDataPath, "Config", "data.db")};upgrade=true")
: new LiteDatabase($"filename={Path.Combine(Bootstrapper.AppDataPath, "Config", "data.db")};upgrade=true;readonly=true");
LiteDatabase globalDatabase;
if (Hacks.IsElevated)
{
globalDatabase = new LiteDatabase($"filename={Path.Combine(Bootstrapper.AppDataPath, "Config", "data.db")};upgrade=true");
}
else
{
if (!File.Exists(Path.Combine(Bootstrapper.AppDataPath, "Config", "data.db")))
{
// 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.
globalDatabase = null;
}
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");
}
}

if (globalDatabase != null)
{
builder.RegisterInstance(globalDatabase).As<LiteDatabase>().SingleInstance().Named<LiteDatabase>(Bootstrapper.GlobalConfigurationDatabaseName);
}

var configService = new ConfigService(globalDatabase, userDatabase);
configService.SetEffectiveConfiguration();
Expand All @@ -58,7 +79,6 @@ protected override void Load(ContainerBuilder builder)

// Since there are two instances of LiteDB, they are added as named instances, so that they can be retrieved when required
builder.RegisterInstance(userDatabase).As<LiteDatabase>().SingleInstance().Named<LiteDatabase>(Bootstrapper.UserConfigurationDatabaseName);
builder.RegisterInstance(globalDatabase).As<LiteDatabase>().SingleInstance().Named<LiteDatabase>(Bootstrapper.GlobalConfigurationDatabaseName);
}
catch (IOException ex)
{
Expand Down

0 comments on commit b02b4dc

Please sign in to comment.