mirror of https://github.com/icedream/npsharp.git
Introducing user numbers (not generated from Brightstar entity identifiers anymore) and using partial database class methods instead to keep the code properly managed
parent
f43f20522f
commit
f30839d297
|
@ -1,4 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using log4net;
|
using log4net;
|
||||||
|
@ -26,25 +27,28 @@ namespace NPSharp.CommandLine.Server
|
||||||
|
|
||||||
public AuthenticationResult AuthenticateUser(NPServerClient client, string token)
|
public AuthenticationResult AuthenticateUser(NPServerClient client, string token)
|
||||||
{
|
{
|
||||||
|
var ar = new AuthenticationResult();
|
||||||
|
|
||||||
// Check if token is valid
|
// Check if token is valid
|
||||||
var resultEnum = _db.Sessions.Where(s => s.Id == token && s.ExpiryTime > DateTime.Now);
|
_db.ValidateSession(token, session =>
|
||||||
if (!resultEnum.Any())
|
{
|
||||||
return new AuthenticationResult(); // authentication failed because token is invalid
|
if (session == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var session = resultEnum.Single();
|
ar =
|
||||||
|
|
||||||
var ar =
|
|
||||||
new AuthenticationResult(new CSteamID
|
new AuthenticationResult(new CSteamID
|
||||||
{
|
{
|
||||||
AccountID = uint.Parse(session.User.Id, NumberStyles.Integer),
|
AccountID = session.User.UserNumber,
|
||||||
AccountInstance = 1,
|
AccountInstance = 1,
|
||||||
AccountType = EAccountType.Individual,
|
AccountType = EAccountType.Individual,
|
||||||
AccountUniverse = EUniverse.Public
|
AccountUniverse = EUniverse.Public
|
||||||
});
|
});
|
||||||
|
|
||||||
_db.DeleteObject(session);
|
_log.DebugFormat("Deleting validated session {0}", session.Id);
|
||||||
|
});
|
||||||
_db.SaveChanges();
|
_db.SaveChanges();
|
||||||
_log.DebugFormat("Deleted now used session {0}", session.Id);
|
|
||||||
|
|
||||||
return ar;
|
return ar;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,20 +1,11 @@
|
||||||
// -----------------------------------------------------------------------
|
using System;
|
||||||
// <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.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using BrightstarDB.Client;
|
using BrightstarDB.Client;
|
||||||
using BrightstarDB.EntityFramework;
|
using BrightstarDB.EntityFramework;
|
||||||
|
|
||||||
namespace NPSharp.CommandLine.Server.Database
|
namespace NPSharp.CommandLine.Server.Database
|
||||||
{
|
{
|
||||||
public class BrightstarDatabaseContext : BrightstarEntityContext
|
public partial class BrightstarDatabaseContext : BrightstarEntityContext
|
||||||
{
|
{
|
||||||
private static readonly EntityMappingStore TypeMappings;
|
private static readonly EntityMappingStore TypeMappings;
|
||||||
|
|
||||||
|
@ -392,6 +383,12 @@ namespace NPSharp.CommandLine.Server.Database
|
||||||
set { SetRelatedProperty("UserMail", value); }
|
set { SetRelatedProperty("UserMail", value); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public UInt32 UserNumber
|
||||||
|
{
|
||||||
|
get { return GetRelatedProperty<UInt32>("UserNumber"); }
|
||||||
|
set { SetRelatedProperty("UserNumber", value); }
|
||||||
|
}
|
||||||
|
|
||||||
public String PasswordHash
|
public String PasswordHash
|
||||||
{
|
{
|
||||||
get { return GetRelatedProperty<String>("PasswordHash"); }
|
get { return GetRelatedProperty<String>("PasswordHash"); }
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace NPSharp.CommandLine.Server.Database
|
||||||
|
{
|
||||||
|
class DatabaseUserExistsException : Exception
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,6 +13,8 @@ namespace NPSharp.CommandLine.Server.Database
|
||||||
|
|
||||||
string UserMail { get; set; }
|
string UserMail { get; set; }
|
||||||
|
|
||||||
|
uint UserNumber { get; set; }
|
||||||
|
|
||||||
string PasswordHash { get; set; }
|
string PasswordHash { get; set; }
|
||||||
|
|
||||||
DateTime LastLogin { get; set; }
|
DateTime LastLogin { get; set; }
|
||||||
|
|
|
@ -33,7 +33,6 @@ namespace NPSharp.CommandLine.Server
|
||||||
|
|
||||||
private static BrightstarDatabaseContext OpenDatabase(string store = "NP")
|
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
|
return
|
||||||
new BrightstarDatabaseContext(
|
new BrightstarDatabaseContext(
|
||||||
"type=embedded;storesdirectory=Database\\;storename=" + store,
|
"type=embedded;storesdirectory=Database\\;storename=" + store,
|
||||||
|
@ -53,21 +52,16 @@ namespace NPSharp.CommandLine.Server
|
||||||
if (db.Users.Count() > 0)
|
if (db.Users.Count() > 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Create first user (test:test)
|
// Create first user
|
||||||
var testUser = db.Users.Create();
|
var testUser = db.CreateUser("test", "test@localhost", "test");
|
||||||
testUser.PasswordHash = BCrypt.Net.BCrypt.HashPassword("test");
|
db.SaveChanges();
|
||||||
testUser.UserMail = "test@localhost";
|
|
||||||
testUser.UserName = "test";
|
|
||||||
|
|
||||||
_log.InfoFormat(
|
_log.InfoFormat(
|
||||||
"Created first user with following details:" + Environment.NewLine + Environment.NewLine +
|
"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,
|
testUser.UserName,
|
||||||
"test");
|
"test");
|
||||||
|
|
||||||
db.SaveChanges();
|
|
||||||
|
|
||||||
_log.DebugFormat("First user id is {0}", testUser.Id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cleanup thread
|
// Cleanup thread
|
||||||
|
@ -78,11 +72,13 @@ namespace NPSharp.CommandLine.Server
|
||||||
using (var dbForCleanup = OpenDatabase())
|
using (var dbForCleanup = OpenDatabase())
|
||||||
{
|
{
|
||||||
_log.Debug("Starting cleanup...");
|
_log.Debug("Starting cleanup...");
|
||||||
|
|
||||||
foreach (var session in dbForCleanup.Sessions.Where(s => s.ExpiryTime < DateTime.Now).ToArray())
|
foreach (var session in dbForCleanup.Sessions.Where(s => s.ExpiryTime < DateTime.Now).ToArray())
|
||||||
{
|
{
|
||||||
_log.DebugFormat("Session {0} became invalid", session.Id);
|
_log.DebugFormat("Session {0} became invalid", session.Id);
|
||||||
dbForCleanup.DeleteObject(session);
|
dbForCleanup.DeleteObject(session);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var ban in dbForCleanup.Bans.Where(s => s.ExpiryTime < DateTime.Now).ToArray())
|
foreach (var ban in dbForCleanup.Bans.Where(s => s.ExpiryTime < DateTime.Now).ToArray())
|
||||||
{
|
{
|
||||||
_log.DebugFormat("Ban {0} became invalid", ban.Id);
|
_log.DebugFormat("Ban {0} became invalid", ban.Id);
|
||||||
|
@ -95,7 +91,6 @@ namespace NPSharp.CommandLine.Server
|
||||||
dbForCleanup.DeleteObject(cheatDetection);
|
dbForCleanup.DeleteObject(cheatDetection);
|
||||||
}
|
}
|
||||||
|
|
||||||
_log.Debug("Saving cleanup...");
|
|
||||||
dbForCleanup.SaveChanges();
|
dbForCleanup.SaveChanges();
|
||||||
|
|
||||||
_log.Debug("Cleanup done.");
|
_log.Debug("Cleanup done.");
|
||||||
|
@ -117,16 +112,12 @@ namespace NPSharp.CommandLine.Server
|
||||||
{
|
{
|
||||||
using (var db = OpenDatabase())
|
using (var db = OpenDatabase())
|
||||||
{
|
{
|
||||||
var matchingUsers =
|
var user = db.GetUser(loginUsername);
|
||||||
db.Users.Where(u => u.UserName == loginUsername).ToArray() // brightstar level
|
|
||||||
.Where(u => BCrypt.Net.BCrypt.Verify(loginPassword, u.PasswordHash)).ToArray() // local level
|
|
||||||
;
|
|
||||||
|
|
||||||
if (!matchingUsers.Any())
|
|
||||||
|
if (user == null || !BCrypt.Net.BCrypt.Verify(loginPassword, user.PasswordHash))
|
||||||
return new SessionAuthenticationResult {Reason = "Invalid credentials"};
|
return new SessionAuthenticationResult {Reason = "Invalid credentials"};
|
||||||
|
|
||||||
var user = matchingUsers.Single();
|
|
||||||
|
|
||||||
// Check for bans
|
// Check for bans
|
||||||
var bans = user.Bans.Where(b => b.ExpiryTime > DateTime.Now).ToArray();
|
var bans = user.Bans.Where(b => b.ExpiryTime > DateTime.Now).ToArray();
|
||||||
if (bans.Any())
|
if (bans.Any())
|
||||||
|
@ -153,9 +144,8 @@ namespace NPSharp.CommandLine.Server
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create user session
|
// Create user session
|
||||||
var session = db.Sessions.Create();
|
var session = db.CreateSession(user);
|
||||||
session.ExpiryTime = DateTime.Now + TimeSpan.FromMinutes(3);
|
_log.DebugFormat("Created session {0}", session.Id);
|
||||||
user.Sessions.Add(session);
|
|
||||||
|
|
||||||
// Update user's last login data
|
// Update user's last login data
|
||||||
user.LastLogin = DateTime.Now;
|
user.LastLogin = DateTime.Now;
|
||||||
|
@ -168,7 +158,7 @@ namespace NPSharp.CommandLine.Server
|
||||||
{
|
{
|
||||||
Success = true,
|
Success = true,
|
||||||
SessionToken = session.Id,
|
SessionToken = session.Id,
|
||||||
UserID = uint.Parse(user.Id, NumberStyles.Integer),
|
UserID = user.UserNumber,
|
||||||
UserMail = user.UserMail,
|
UserMail = user.UserMail,
|
||||||
UserName = user.UserName
|
UserName = user.UserName
|
||||||
};
|
};
|
||||||
|
|
|
@ -74,6 +74,8 @@
|
||||||
</Reference>
|
</Reference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Compile Include="Database\BrightstarDatabaseContext.custom.cs" />
|
||||||
|
<Compile Include="Database\DatabaseUserExistsException.cs" />
|
||||||
<Compile Include="Database\IBan.cs" />
|
<Compile Include="Database\IBan.cs" />
|
||||||
<Compile Include="Database\ICheatDetection.cs" />
|
<Compile Include="Database\ICheatDetection.cs" />
|
||||||
<Compile Include="Database\IFriend.cs" />
|
<Compile Include="Database\IFriend.cs" />
|
||||||
|
|
Loading…
Reference in New Issue