Skip to content

Commit

Permalink
Simplify to only create default admin when no users.
Browse files Browse the repository at this point in the history
  • Loading branch information
grilledham committed Aug 22, 2020
1 parent a11ae16 commit 0241d51
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 275 deletions.
15 changes: 6 additions & 9 deletions FactorioWebInterface/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;
using Serilog;
using Serilog.Events;
using System;
Expand Down Expand Up @@ -39,13 +38,11 @@ public static async Task Main(string[] args)
Log.Information("Starting factorio web interface");

var host = CreateWebHostBuilder(args).Build();

// This makes sure the databases are setup.
SeedData(host);

var services = host.Services;

await Task.WhenAll(
// This makes sure the databases are setup.
SeedData(host),
services.GetService<IFactorioServerDataService>().Init(),
services.GetService<DiscordBot>().Init(),
services.GetService<IDiscordService>().Init());
Expand Down Expand Up @@ -80,7 +77,7 @@ public static IHostBuilder CreateWebHostBuilder(string[] args) =>
.UseSerilog();
});

private static void SeedData(IHost host)
private static async Task SeedData(IHost host)
{
using (var scope = host.Services.CreateScope())
{
Expand All @@ -94,10 +91,10 @@ private static void SeedData(IHost host)

var roleManager = services.GetService<RoleManager<IdentityRole>>();

roleManager.CreateAsync(new IdentityRole(Constants.RootRole));
roleManager.CreateAsync(new IdentityRole(Constants.AdminRole));
await roleManager.CreateAsync(new IdentityRole(Constants.RootRole));
await roleManager.CreateAsync(new IdentityRole(Constants.AdminRole));

_ = services.GetService<IDefaultAdminAccountService>().SetupDefaultUserAsync();
await services.GetService<IDefaultAdminAccountService>().SetupDefaultUserAsync();
}
}
}
Expand Down
139 changes: 18 additions & 121 deletions FactorioWebInterface/Services/DefaultAdminAccountService.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
using FactorioWebInterface.Data;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using System;
using System.IO;
using System.IO.Abstractions;
using System.Linq;
using System.Threading.Tasks;
Expand All @@ -27,170 +27,67 @@ public DefaultAdminAccountService(UserManager<ApplicationUser> userManager, ILog
_fileSystem = fileSystem;
}

public enum AccountsNumbers
{
NoAccounts,
NoRootAccount,
DefaultIsOnlyRootAccount,
MultipleAccounts
}

public async Task SetupDefaultUserAsync()
{
string id = Constants.DefaultAdminAccount;

await ValidateOrClearDefaultUserAsync(id);

switch (await OnlyAccount())
if (await NoUsers())
{
case AccountsNumbers.DefaultIsOnlyRootAccount:
_logger.LogInformation("{UserId} could not be created, another account already exists", Constants.DefaultAdminAccount);
return;
case AccountsNumbers.MultipleAccounts:
await ValidateOrClearDefaultUserAsync(id, true);
return;
case AccountsNumbers.NoAccounts:
break;
case AccountsNumbers.NoRootAccount:
break;
await CreateDefaultUserAsync();
}

await CreateDefaultUserAsync(id);
}

//Suggestion: Perform count directly on database (Eg. using LINQ or SQL)
public async Task<AccountsNumbers> OnlyAccount()
private async Task<bool> NoUsers()
{

var rootUsers = await _userManager.GetUsersInRoleAsync(Constants.RootRole);
int rootCount = rootUsers.Count();

if (rootCount == 1 && await ValidateDefaultUserAsync(rootUsers.First()))
{
return AccountsNumbers.DefaultIsOnlyRootAccount;
}

var users = _userManager.Users;
int userCount = users.Count();

if (userCount > 0 && rootCount > 0)
{
return AccountsNumbers.MultipleAccounts;
}

if (userCount == 0)
{
return AccountsNumbers.NoAccounts;
}

return AccountsNumbers.NoRootAccount;
int count = await _userManager.Users.CountAsync();
return count == 0;
}

public async Task ValidateOrClearDefaultUserAsync(string id, bool force = false)
{
ApplicationUser userResult = await _userManager.FindByIdAsync(id);
if (await ValidateDefaultUserAsync(userResult) && !force)
{
_logger.LogInformation("Valid {UserId} already exists", Constants.DefaultAdminAccount);
return;
}

if (userResult != null)
{
var deleteResult = await _userManager.DeleteAsync(userResult);
if (!deleteResult.Succeeded)
{
_logger.LogError("{UserId} couldn't be deleted! This may pose a security risk for your application. Will attempt to delete again at next reboot", Constants.DefaultAdminAccount);
}
_logger.LogInformation("{UserId} deleted", Constants.DefaultAdminAccount);
DeleteDefaultAccountFile();
}
}

public async Task<bool> ValidateDefaultUserAsync(ApplicationUser user)
{
if (user == null || user.UserName != Constants.DefaultAdminName)
{
return false;
}

if (!await _userManager.HasPasswordAsync(user))
{
return false;
}

if (!await _userManager.IsInRoleAsync(user, Constants.RootRole))
{
return false;
}

return true;
}

private async Task CreateDefaultUserAsync(string id)
private async Task CreateDefaultUserAsync()
{
ApplicationUser user = new ApplicationUser()
{
Id = id,
Id = Constants.DefaultAdminAccount,
UserName = Constants.DefaultAdminName
};

var result = await _userManager.CreateAsync(user);
if (!result.Succeeded)
{
_logger.LogError("Couldn't create {UserId}", Constants.DefaultAdminAccount);
_logger.LogError("Could not create {UserId}", Constants.DefaultAdminAccount);
return;
}

result = await _userManager.AddToRoleAsync(user, Constants.RootRole);
if (!result.Succeeded)
{
_logger.LogError("Couldn't add role to {UserId}", Constants.DefaultAdminAccount);
_logger.LogError("Could not add role to {UserId}", Constants.DefaultAdminAccount);
return;
}

result = await _userManager.AddToRoleAsync(user, Constants.AdminRole);
if (!result.Succeeded)
{
_logger.LogError("Couldn't add role to {UserId}", Constants.DefaultAdminAccount);
_logger.LogError("Could not add role to {UserId}", Constants.DefaultAdminAccount);
return;
}

string password = Guid.NewGuid().ToString();
result = await _userManager.AddPasswordAsync(user, password);
if (!result.Succeeded)
{
_logger.LogError("Couldn't add password to {UserId} ", Constants.DefaultAdminAccount);
_logger.LogError("Could not add password to {UserId} ", Constants.DefaultAdminAccount);
return;
}
_logger.LogWarning("{UserId} created. This action potential exposes your interface, creating a new account and restarting this web interface will disable the default admin account", Constants.DefaultAdminAccount);
_logger.LogWarning("{UserId} created, see {passwordFile} for password. It is recommended to change the {UserId} password on the user's account page", Constants.DefaultAdminAccount, Constants.DefaultAdminFile, Constants.DefaultAdminAccount);

string warningTag = "! - Warning - !";
var path = GetDefaultAccountFilePath();
DeleteDefaultAccountFile();
_fileSystem.File.WriteAllText(path, $"{warningTag}\nThis account is unsecure. Please setup a personal account.\n{warningTag}\nUsername: {Constants.DefaultAdminName}\nPassword: {password}");
}

public void DeleteDefaultAccountFile()
{
var path = GetDefaultAccountFilePath();
try
{
_fileSystem.File.Delete(path);
}
catch
{
_logger.LogInformation("Couldn't delete DefaultAccount file");
return;
}
_logger.LogInformation("DefaultAccount file deleted");
const string warningTag = "! - Warning - !";
string path = GetDefaultAccountFilePath();
_fileSystem.File.WriteAllText(path, $"{warningTag}\nIt is recommended to change the {Constants.DefaultAdminName} password on the user's account page and to delete this file.\n{warningTag}\nUsername: {Constants.DefaultAdminName}\nPassword: {password}");
}

private static string GetDefaultAccountFilePath()
private string GetDefaultAccountFilePath()
{
string path = AppDomain.CurrentDomain.BaseDirectory!;
path = Path.Combine(path, Constants.DefaultAdminFile);
return path;
return _fileSystem.Path.Combine(path, Constants.DefaultAdminFile);
}
}
}
Loading

0 comments on commit 0241d51

Please sign in to comment.