diff --git a/src/npserv/BrightstarDatabaseAuthenticationHandler.cs b/src/npserv/BrightstarDatabaseAuthenticationHandler.cs
index fd623e0..0f3d437 100644
--- a/src/npserv/BrightstarDatabaseAuthenticationHandler.cs
+++ b/src/npserv/BrightstarDatabaseAuthenticationHandler.cs
@@ -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
-
- var session = resultEnum.Single();
-
- var ar =
- new AuthenticationResult(new CSteamID
+ _db.ValidateSession(token, session =>
+ {
+ if (session == null)
{
- AccountID = uint.Parse(session.User.Id, NumberStyles.Integer),
- AccountInstance = 1,
- AccountType = EAccountType.Individual,
- AccountUniverse = EUniverse.Public
- });
+ return;
+ }
- _db.DeleteObject(session);
+ ar =
+ new AuthenticationResult(new CSteamID
+ {
+ AccountID = session.User.UserNumber,
+ AccountInstance = 1,
+ AccountType = EAccountType.Individual,
+ AccountUniverse = EUniverse.Public
+ });
+
+ _log.DebugFormat("Deleting validated session {0}", session.Id);
+ });
_db.SaveChanges();
- _log.DebugFormat("Deleted now used session {0}", session.Id);
return ar;
}
diff --git a/src/npserv/Database/BrightstarDatabaseContext.cs b/src/npserv/Database/BrightstarDatabaseContext.cs
index 8a39b69..ae1d7fb 100644
--- a/src/npserv/Database/BrightstarDatabaseContext.cs
+++ b/src/npserv/Database/BrightstarDatabaseContext.cs
@@ -1,20 +1,11 @@
-// -----------------------------------------------------------------------
-//
-// This code was generated from a template.
-//
-// Changes to this file may cause incorrect behaviour and will be lost
-// if the code is regenerated.
-//
-//------------------------------------------------------------------------
-
-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("UserNumber"); }
+ set { SetRelatedProperty("UserNumber", value); }
+ }
+
public String PasswordHash
{
get { return GetRelatedProperty("PasswordHash"); }
diff --git a/src/npserv/Database/BrightstarDatabaseContext.custom.cs b/src/npserv/Database/BrightstarDatabaseContext.custom.cs
new file mode 100644
index 0000000..7185cd7
--- /dev/null
+++ b/src/npserv/Database/BrightstarDatabaseContext.custom.cs
@@ -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;
+ }
+
+ ///
+ /// Creates a user session.
+ ///
+ /// The user to assign the session to.
+ /// The time span in seconds. Default: 3 minutes.
+ /// The newly created user session
+ 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;
+ }
+
+ ///
+ /// Tries to find the wanted session and drops it if it's valid,
+ /// therefore "using it".
+ ///
+ /// The token of the wanted session
+ /// The callback to use for session results (goes for both invalid and valid sessions)
+ /// The found session if the session is validated successfully, otherwise null.
+ public void ValidateSession(string sessionToken, Action 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);
+ }
+ }
+ }
+}
diff --git a/src/npserv/Database/DatabaseUserExistsException.cs b/src/npserv/Database/DatabaseUserExistsException.cs
new file mode 100644
index 0000000..5f7cea7
--- /dev/null
+++ b/src/npserv/Database/DatabaseUserExistsException.cs
@@ -0,0 +1,8 @@
+using System;
+
+namespace NPSharp.CommandLine.Server.Database
+{
+ class DatabaseUserExistsException : Exception
+ {
+ }
+}
diff --git a/src/npserv/Database/IUser.cs b/src/npserv/Database/IUser.cs
index ad2315e..35a0fd0 100644
--- a/src/npserv/Database/IUser.cs
+++ b/src/npserv/Database/IUser.cs
@@ -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; }
diff --git a/src/npserv/Program.cs b/src/npserv/Program.cs
index 5e8dc34..92f4fc4 100644
--- a/src/npserv/Program.cs
+++ b/src/npserv/Program.cs
@@ -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
};
diff --git a/src/npserv/npserv.csproj b/src/npserv/npserv.csproj
index 417427c..afca0e9 100644
--- a/src/npserv/npserv.csproj
+++ b/src/npserv/npserv.csproj
@@ -74,6 +74,8 @@
+
+