tgstation-server  4.4.0
The /tg/station 13 server suite
DatabaseSeeder.cs
Go to the documentation of this file.
1 using Microsoft.EntityFrameworkCore;
2 using Microsoft.Extensions.Logging;
3 using Microsoft.Extensions.Options;
4 using System;
5 using System.Linq;
6 using System.Threading;
7 using System.Threading.Tasks;
13 using Z.EntityFramework.Plus;
14 
15 namespace Tgstation.Server.Host.Database
16 {
19  {
24 
29 
33  readonly ILogger<DatabaseContext> databaseLogger;
34 
38  readonly ILogger<DatabaseSeeder> logger;
39 
44 
49 
60  ICryptographySuite cryptographySuite,
61  IPlatformIdentifier platformIdentifier,
62  IOptions<GeneralConfiguration> generalConfigurationOptions,
63  IOptions<DatabaseConfiguration> databaseConfigurationOptions,
64  ILogger<DatabaseContext> databaseLogger,
65  ILogger<DatabaseSeeder> logger)
66  {
67  this.cryptographySuite = cryptographySuite ?? throw new ArgumentNullException(nameof(cryptographySuite));
68  this.platformIdentifier = platformIdentifier ?? throw new ArgumentNullException(nameof(platformIdentifier));
69  databaseConfiguration = databaseConfigurationOptions?.Value ?? throw new ArgumentNullException(nameof(databaseConfigurationOptions));
70  generalConfiguration = generalConfigurationOptions?.Value ?? throw new ArgumentNullException(nameof(generalConfigurationOptions));
71  this.databaseLogger = databaseLogger ?? throw new ArgumentNullException(nameof(databaseLogger));
72  this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
73  }
74 
79  void SeedAdminUser(IDatabaseContext databaseContext)
80  {
81  var admin = new User
82  {
84  CreatedAt = DateTimeOffset.Now,
86  Name = Api.Models.User.AdminName,
87  CanonicalName = User.CanonicalizeName(Api.Models.User.AdminName),
88  Enabled = true,
89  };
90  cryptographySuite.SetUserPassword(admin, Api.Models.User.DefaultAdminPassword, true);
91  databaseContext.Users.Add(admin);
92  }
93 
100  async Task SeedDatabase(IDatabaseContext databaseContext, CancellationToken cancellationToken)
101  {
102  SeedAdminUser(databaseContext);
103  await databaseContext.Save(cancellationToken).ConfigureAwait(false);
104  }
105 
112  async Task SanitizeDatabase(IDatabaseContext databaseContext, CancellationToken cancellationToken)
113  {
114  var admin = await GetAdminUser(databaseContext, cancellationToken).ConfigureAwait(false);
115  if (admin != null)
116  {
117  // Fix the issue with ulong enums
118  // https://github.com/tgstation/tgstation-server/commit/db341d43b3dab74fe3681f5172ca9bfeaafa6b6d#diff-09f06ec4584665cf89bb77b97f5ccfb9R36-R39
119  // https://github.com/JamesNK/Newtonsoft.Json/issues/2301
120  admin.AdministrationRights &= RightsHelper.AllRights<AdministrationRights>();
121  admin.InstanceManagerRights &= RightsHelper.AllRights<InstanceManagerRights>();
122  }
123 
124  if (platformIdentifier.IsWindows)
125  {
126  // normalize backslashes to forward slashes
127  var allInstances = await databaseContext
128  .Instances
129  .AsQueryable()
130  .ToListAsync(cancellationToken)
131  .ConfigureAwait(false);
132  foreach (var instance in allInstances)
133  instance.Path = instance.Path.Replace('\\', '/');
134  }
135 
136  var ids = await databaseContext
138  .AsQueryable()
139  .Where(x => x.TopicRequestTimeout == 0)
140  .Select(x => x.Id)
141  .ToListAsync(cancellationToken)
142  .ConfigureAwait(false);
143 
144  var rowsUpdated = ids.Count;
145  foreach (var id in ids)
146  {
147  var newDDSettings = new DreamDaemonSettings
148  {
149  Id = id
150  };
151 
152  databaseContext.DreamDaemonSettings.Attach(newDDSettings);
153  newDDSettings.TopicRequestTimeout = generalConfiguration.ByondTopicTimeout;
154  }
155 
156  if (rowsUpdated > 0)
157  logger.LogInformation(
158  "Updated {0} instances to use database backed BYOND topic timeouts from configuration setting of {1}",
159  rowsUpdated,
160  generalConfiguration.ByondTopicTimeout);
161 
162  await databaseContext.Save(cancellationToken).ConfigureAwait(false);
163  }
164 
171  async Task ResetAdminPassword(IDatabaseContext databaseContext, CancellationToken cancellationToken)
172  {
173  var admin = await GetAdminUser(databaseContext, cancellationToken).ConfigureAwait(false);
174  if (admin != null)
175  {
176  admin.Enabled = true;
177  cryptographySuite.SetUserPassword(admin, Api.Models.User.DefaultAdminPassword, false);
178  }
179 
180  await databaseContext.Save(cancellationToken).ConfigureAwait(false);
181  }
182 
189  async Task<User> GetAdminUser(IDatabaseContext databaseContext, CancellationToken cancellationToken)
190  {
191  var admin = await databaseContext
192  .Users
193  .AsQueryable()
194  .Where(x => x.CanonicalName == User.CanonicalizeName(Api.Models.User.AdminName))
195  .FirstOrDefaultAsync(cancellationToken)
196  .ConfigureAwait(false);
197  if (admin == default)
198  SeedAdminUser(databaseContext);
199 
200  return admin;
201  }
202 
204  public async Task Initialize(IDatabaseContext databaseContext, CancellationToken cancellationToken)
205  {
206  if (databaseContext == null)
207  throw new ArgumentNullException(nameof(databaseContext));
208 
209  if (databaseConfiguration.DropDatabase)
210  {
211  logger.LogCritical("DropDatabase configuration option set! Dropping any existing database...");
212  await databaseContext.Drop(cancellationToken).ConfigureAwait(false);
213  }
214 
215  var wasEmpty = await databaseContext.Migrate(databaseLogger, cancellationToken).ConfigureAwait(false);
216  if (wasEmpty)
217  {
218  logger.LogInformation("Seeding database...");
219  await SeedDatabase(databaseContext, cancellationToken).ConfigureAwait(false);
220  }
221  else
222  {
223  if (databaseConfiguration.ResetAdminPassword)
224  {
225  logger.LogWarning("Enabling and resetting admin password due to configuration!");
226  await ResetAdminPassword(databaseContext, cancellationToken).ConfigureAwait(false);
227  }
228 
229  await SanitizeDatabase(databaseContext, cancellationToken).ConfigureAwait(false);
230  }
231  }
232 
234  public Task Downgrade(IDatabaseContext databaseContext, Version downgradeVersion, CancellationToken cancellationToken)
235  {
236  if (databaseContext == null)
237  throw new ArgumentNullException(nameof(databaseContext));
238  if (downgradeVersion == null)
239  throw new ArgumentNullException(nameof(downgradeVersion));
240 
241  return databaseContext.SchemaDowngradeForServerVersion(databaseLogger, downgradeVersion, databaseConfiguration.DatabaseType, cancellationToken);
242  }
243  }
244 }
Task SchemaDowngradeForServerVersion(ILogger< DatabaseContext > logger, Version version, DatabaseType currentDatabaseType, CancellationToken cancellationToken)
Attempt to downgrade the schema to the migration used for a given server version ...
readonly GeneralConfiguration generalConfiguration
The GeneralConfiguration for the DatabaseSeeder.
Task Drop(CancellationToken cancellationToken)
Attempts to delete all tables and drop the database in use.
readonly ICryptographySuite cryptographySuite
The ICryptographySuite for the DatabaseSeeder
DatabaseSeeder(ICryptographySuite cryptographySuite, IPlatformIdentifier platformIdentifier, IOptions< GeneralConfiguration > generalConfigurationOptions, IOptions< DatabaseConfiguration > databaseConfigurationOptions, ILogger< DatabaseContext > databaseLogger, ILogger< DatabaseSeeder > logger)
Construct a DatabaseSeeder
For initially setting up a database.
async Task SanitizeDatabase(IDatabaseContext databaseContext, CancellationToken cancellationToken)
Correct invalid database data caused by previous versions.
void Add(TModel model)
Add a given model to the the working set.
async Task SeedDatabase(IDatabaseContext databaseContext, CancellationToken cancellationToken)
Initially seed a given databaseContext
AdministrationRights
Rights for Models.Administration
uint TopicRequestTimeout
The timeout for sending and receiving BYOND topics in milliseconds.
Contains various cryptographic functions
async Task Initialize(IDatabaseContext databaseContext, CancellationToken cancellationToken)
Setup up a given databaseContext .
IDatabaseCollection< Instance > Instances
The Instances in the IDatabaseContext
async Task ResetAdminPassword(IDatabaseContext databaseContext, CancellationToken cancellationToken)
Changes the admin password in IDatabaseContext back to it&#39;s default and enables the account ...
InstanceManagerRights
Rights for managing Models.Instances
static string CanonicalizeName(string name)
Change a Api.Models.Internal.User.Name into a CanonicalName.
readonly ILogger< DatabaseContext > databaseLogger
The ILogger used for IDatabaseContexts.
readonly DatabaseConfiguration databaseConfiguration
The DatabaseConfiguration for the DatabaseSeeder.
readonly IPlatformIdentifier platformIdentifier
The IPlatformIdentifier for the DatabaseSeeder.
Configuration options for the Database.DatabaseContext
Task< bool > Migrate(ILogger< DatabaseContext > logger, CancellationToken cancellationToken)
Creates and migrates the IDatabaseContext
readonly ILogger< DatabaseSeeder > logger
The ILogger for the DatabaseSeeder.
Task Downgrade(IDatabaseContext databaseContext, Version downgradeVersion, CancellationToken cancellationToken)
Migrate a given databaseContext down.
Task Save(CancellationToken cancellationToken)
Saves changes made to the IDatabaseContext
async Task< User > GetAdminUser(IDatabaseContext databaseContext, CancellationToken cancellationToken)
Get or create the admin User.
IDatabaseCollection< DreamDaemonSettings > DreamDaemonSettings
The Models.DreamDaemonSettings in the IDatabaseContext
void SeedAdminUser(IDatabaseContext databaseContext)
Add a default admin User to a given databaseContext
IDatabaseCollection< User > Users
The Users in the IDatabaseContext
For identifying the current platform