using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.ChangeTracking; using NexusForever.Database.Auth.Model; using NexusForever.Database.Configuration; using NLog; namespace NexusForever.Database.Auth { public class AuthDatabase { private static readonly ILogger log = LogManager.GetCurrentClassLogger(); private readonly IDatabaseConfig config; public AuthDatabase(IDatabaseConfig config) { this.config = config; } public async Task Save(Action action) { using var context = new AuthContext(config); action.Invoke(context); await context.SaveChangesAsync(); } public void Migrate() { using var context = new AuthContext(config); List migrations = context.Database.GetPendingMigrations().ToList(); if (migrations.Count > 0) { log.Info($"Applying {migrations.Count} authentication database migration(s)..."); foreach (string migration in migrations) log.Info(migration); context.Database.Migrate(); } } /// /// Selects an asynchronously that matches the supplied email. /// public async Task GetAccountByEmailAsync(string email) { using var context = new AuthContext(config); return await context.Account.SingleOrDefaultAsync(a => a.Email == email); } /// /// Selects an asynchronously that matches the supplied email and game token. /// public async Task GetAccountByGameTokenAsync(string email, string gameToken) { using var context = new AuthContext(config); return await context.Account.SingleOrDefaultAsync(a => a.Email == email && a.GameToken == gameToken); } /// /// Selects an asynchronously that matches the supplied email and session key. /// public async Task GetAccountBySessionKeyAsync(string email, string sessionKey) { using var context = new AuthContext(config); return await context.Account .AsSplitQuery() .Include(a => a.AccountCostumeUnlock) .Include(a => a.AccountCurrency) .Include(a => a.AccountGenericUnlock) .Include(a => a.AccountKeybinding) .Include(a => a.AccountEntitlement) .Include(a => a.AccountPermission) .Include(a => a.AccountRole) .SingleOrDefaultAsync(a => a.Email == email && a.SessionKey == sessionKey); } /// /// Returns if an account with the given username already exists. /// public bool AccountExists(string email) { using var context = new AuthContext(config); return context.Account.SingleOrDefault(a => a.Email == email) != null; } /// /// Create a new account with the supplied email, salt and password verifier that is inserted into the database. /// public void CreateAccount(string email, string s, string v, uint role) { if (AccountExists(email)) throw new InvalidOperationException($"Account with that username already exists."); using var context = new AuthContext(config); var model = new AccountModel { Email = email, S = s, V = v }; model.AccountRole.Add(new AccountRoleModel { RoleId = role }); context.Account.Add(model); context.SaveChanges(); } /// /// Delete an existing account with the supplied email. /// public bool DeleteAccount(string email) { using var context = new AuthContext(config); AccountModel account = context.Account.SingleOrDefault(a => a.Email == email); if (account == null) return false; context.Account.Remove(account); return context.SaveChanges() > 0; } /// /// Update with supplied game token asynchronously. /// public async Task UpdateAccountGameToken(AccountModel account, string gameToken) { account.GameToken = gameToken; using var context = new AuthContext(config); EntityEntry entity = context.Attach(account); entity.Property(p => p.GameToken).IsModified = true; await context.SaveChangesAsync(); } /// /// Update with supplied session key asynchronously. /// public async Task UpdateAccountSessionKey(AccountModel account, string sessionKey) { account.SessionKey = sessionKey; await using var context = new AuthContext(config); EntityEntry entity = context.Attach(account); entity.Property(p => p.SessionKey).IsModified = true; await context.SaveChangesAsync(); } public ImmutableList GetServers() { using var context = new AuthContext(config); return context.Server .AsNoTracking() .ToImmutableList(); } public ImmutableList GetServerMessages() { using var context = new AuthContext(config); return context.ServerMessage .AsNoTracking() .ToImmutableList(); } public ImmutableList GetPermissions() { using var context = new AuthContext(config); return context.Permission .AsNoTracking() .ToImmutableList(); } public ImmutableList GetRoles() { using var context = new AuthContext(config); return context.Role .Include(r => r.RolePermission) .AsNoTracking() .ToImmutableList(); } } }