Introducing user numbers (not generated from Brightstar entity identifiers anymore) and using partial database class methods instead to keep the code properly managed

feature-npv2
Icedream 2014-05-15 06:28:31 +02:00
parent f43f20522f
commit f30839d297
7 changed files with 148 additions and 49 deletions

View File

@ -1,4 +1,5 @@
using System;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using log4net;
@ -26,25 +27,28 @@ namespace NPSharp.CommandLine.Server
public AuthenticationResult AuthenticateUser(NPServerClient client, string token)
{
var ar = new AuthenticationResult();
// Check if token is valid
var resultEnum = _db.Sessions.Where(s => s.Id == token && s.ExpiryTime > DateTime.Now);
if (!resultEnum.Any())
return new AuthenticationResult(); // authentication failed because token is invalid
_db.ValidateSession(token, session =>
{
if (session == null)
{
return;
}
var session = resultEnum.Single();
var ar =
ar =
new AuthenticationResult(new CSteamID
{
AccountID = uint.Parse(session.User.Id, NumberStyles.Integer),
AccountID = session.User.UserNumber,
AccountInstance = 1,
AccountType = EAccountType.Individual,
AccountUniverse = EUniverse.Public
});
_db.DeleteObject(session);
_log.DebugFormat("Deleting validated session {0}", session.Id);
});
_db.SaveChanges();
_log.DebugFormat("Deleted now used session {0}", session.Id);
return ar;
}

View File

@ -1,20 +1,11 @@
// -----------------------------------------------------------------------
// <autogenerated>
// This code was generated from a template.
//
// Changes to this file may cause incorrect behaviour and will be lost
// if the code is regenerated.
// </autogenerated>
//------------------------------------------------------------------------
using System;
using System;
using System.Collections.Generic;
using BrightstarDB.Client;
using BrightstarDB.EntityFramework;
namespace NPSharp.CommandLine.Server.Database
{
public class BrightstarDatabaseContext : BrightstarEntityContext
public partial class BrightstarDatabaseContext : BrightstarEntityContext
{
private static readonly EntityMappingStore TypeMappings;
@ -392,6 +383,12 @@ namespace NPSharp.CommandLine.Server.Database
set { SetRelatedProperty("UserMail", value); }
}
public UInt32 UserNumber
{
get { return GetRelatedProperty<UInt32>("UserNumber"); }
set { SetRelatedProperty("UserNumber", value); }
}
public String PasswordHash
{
get { return GetRelatedProperty<String>("PasswordHash"); }

View File

@ -0,0 +1,96 @@
using System;
using System.Linq;
namespace NPSharp.CommandLine.Server.Database
{
public partial class BrightstarDatabaseContext
{
public IUser CreateUser(string name, string email, string password)
{
if (UserExists(name))
throw new DatabaseUserExistsException();
var user = Users.Create();
user.UserName = name;
user.UserMail = email;
user.PasswordHash = BCrypt.Net.BCrypt.HashPassword(password);
user.UserNumber = _genUserNumber(/*user.Id*/);
return user;
}
private uint _genUserNumber(/*string userId*/)
{
/*
// for some reason we sometimes get full URIs here.
userId = userId.Split('/').Last().Replace("-", "");
// Since the string is a hexified UNIQUE identifier,
// use the numeric representation of it.
var userNum = uint.Parse(userId, NumberStyles.HexNumber);
*/
// The above doesn't work since the GUID has a few bits too much :P
// So instead - even though taking more queries - we will use the user
// count to approximate a new user ID.
var userNum = (uint)Users.Count() + 1;
while (Users.Count(u => u.UserNumber == userNum) > 0)
userNum++;
return userNum;
}
public bool UserExists(string userName)
{
return GetUser(userName) != null;
}
public IUser GetUser(string userName)
{
var users = Users.Where(u => u.UserName == userName).ToArray();
return users.Any() ? users.Single() : null;
}
/// <summary>
/// Creates a user session.
/// </summary>
/// <param name="user">The user to assign the session to.</param>
/// <param name="validTimeSpan">The time span in seconds. Default: 3 minutes.</param>
/// <returns>The newly created user session</returns>
public ISession CreateSession(IUser user, uint validTimeSpan = 3 * 60)
{
var session = Sessions.Create();
session.ExpiryTime = DateTime.Now + TimeSpan.FromSeconds(validTimeSpan);
user.Sessions.Add(session);
return session;
}
/// <summary>
/// Tries to find the wanted session and drops it if it's valid,
/// therefore "using it".
/// </summary>
/// <param name="sessionToken">The token of the wanted session</param>
/// <param name="callback">The callback to use for session results (goes for both invalid and valid sessions)</param>
/// <returns>The found session if the session is validated successfully, otherwise null.</returns>
public void ValidateSession(string sessionToken, Action<ISession> callback)
{
var sessions = Sessions
.Where(s => s.Id == sessionToken).ToArray() // database level query
.Where(s => s.ExpiryTime > DateTime.Now).ToArray(); // local level query (seems like this isn't supported [yet])
// We have to use a callback here since deleting the object from database
// will also release it from .NET's management and therefore makes the object
// invalid.
if (!sessions.Any())
callback(null);
else
{
var session = sessions.Single();
callback(session);
DeleteObject(session);
}
}
}
}

View File

@ -0,0 +1,8 @@
using System;
namespace NPSharp.CommandLine.Server.Database
{
class DatabaseUserExistsException : Exception
{
}
}

View File

@ -13,6 +13,8 @@ namespace NPSharp.CommandLine.Server.Database
string UserMail { get; set; }
uint UserNumber { get; set; }
string PasswordHash { get; set; }
DateTime LastLogin { get; set; }

View File

@ -33,7 +33,6 @@ namespace NPSharp.CommandLine.Server
private static BrightstarDatabaseContext OpenDatabase(string store = "NP")
{
// TODO: This line is CREATING a new database but it's supposed to open it only if it's already created. Look up!
return
new BrightstarDatabaseContext(
"type=embedded;storesdirectory=Database\\;storename=" + store,
@ -53,21 +52,16 @@ namespace NPSharp.CommandLine.Server
if (db.Users.Count() > 0)
return;
// Create first user (test:test)
var testUser = db.Users.Create();
testUser.PasswordHash = BCrypt.Net.BCrypt.HashPassword("test");
testUser.UserMail = "test@localhost";
testUser.UserName = "test";
// Create first user
var testUser = db.CreateUser("test", "test@localhost", "test");
db.SaveChanges();
_log.InfoFormat(
"Created first user with following details:" + Environment.NewLine + Environment.NewLine +
"Username: {0}" + Environment.NewLine + "Password: {1}",
"Username: {0}" + Environment.NewLine +
"Password: {1}" + Environment.NewLine,
testUser.UserName,
"test");
db.SaveChanges();
_log.DebugFormat("First user id is {0}", testUser.Id);
}
// Cleanup thread
@ -78,11 +72,13 @@ namespace NPSharp.CommandLine.Server
using (var dbForCleanup = OpenDatabase())
{
_log.Debug("Starting cleanup...");
foreach (var session in dbForCleanup.Sessions.Where(s => s.ExpiryTime < DateTime.Now).ToArray())
{
_log.DebugFormat("Session {0} became invalid", session.Id);
dbForCleanup.DeleteObject(session);
}
foreach (var ban in dbForCleanup.Bans.Where(s => s.ExpiryTime < DateTime.Now).ToArray())
{
_log.DebugFormat("Ban {0} became invalid", ban.Id);
@ -95,7 +91,6 @@ namespace NPSharp.CommandLine.Server
dbForCleanup.DeleteObject(cheatDetection);
}
_log.Debug("Saving cleanup...");
dbForCleanup.SaveChanges();
_log.Debug("Cleanup done.");
@ -117,16 +112,12 @@ namespace NPSharp.CommandLine.Server
{
using (var db = OpenDatabase())
{
var matchingUsers =
db.Users.Where(u => u.UserName == loginUsername).ToArray() // brightstar level
.Where(u => BCrypt.Net.BCrypt.Verify(loginPassword, u.PasswordHash)).ToArray() // local level
;
var user = db.GetUser(loginUsername);
if (!matchingUsers.Any())
if (user == null || !BCrypt.Net.BCrypt.Verify(loginPassword, user.PasswordHash))
return new SessionAuthenticationResult {Reason = "Invalid credentials"};
var user = matchingUsers.Single();
// Check for bans
var bans = user.Bans.Where(b => b.ExpiryTime > DateTime.Now).ToArray();
if (bans.Any())
@ -153,9 +144,8 @@ namespace NPSharp.CommandLine.Server
}
// Create user session
var session = db.Sessions.Create();
session.ExpiryTime = DateTime.Now + TimeSpan.FromMinutes(3);
user.Sessions.Add(session);
var session = db.CreateSession(user);
_log.DebugFormat("Created session {0}", session.Id);
// Update user's last login data
user.LastLogin = DateTime.Now;
@ -168,7 +158,7 @@ namespace NPSharp.CommandLine.Server
{
Success = true,
SessionToken = session.Id,
UserID = uint.Parse(user.Id, NumberStyles.Integer),
UserID = user.UserNumber,
UserMail = user.UserMail,
UserName = user.UserName
};

View File

@ -74,6 +74,8 @@
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="Database\BrightstarDatabaseContext.custom.cs" />
<Compile Include="Database\DatabaseUserExistsException.cs" />
<Compile Include="Database\IBan.cs" />
<Compile Include="Database\ICheatDetection.cs" />
<Compile Include="Database\IFriend.cs" />