Add authentication support (#185)

This commit is contained in:
Michael Green
2023-11-25 14:50:44 +11:00
committed by GitHub
parent 2ade60c551
commit 3d2f94681a
88 changed files with 6187 additions and 124 deletions
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Data;
using gaseous_server.Classes;
using Microsoft.AspNetCore.Identity;
namespace Authentication
{
/// <summary>
/// Class that implements the ASP.NET Identity
/// IRole interface
/// </summary>
public class ApplicationRole : IdentityRole
{
}
}
@@ -0,0 +1,15 @@
using gaseous_server.Classes;
using Microsoft.AspNetCore.Identity;
using System;
namespace Authentication
{
/// <summary>
/// Class that implements the ASP.NET Identity
/// IUser interface
/// </summary>
public class ApplicationUser : IdentityUser
{
public SecurityProfileViewModel SecurityProfile { get; set; }
}
}
@@ -0,0 +1,171 @@
using System;
using System.Security.Claims;
using System.Threading.Tasks;
using gaseous_server.Classes;
using Microsoft.AspNetCore.Identity;
using MySqlConnector;
namespace Authentication
{
/// <summary>
/// Class that implements the key ASP.NET Identity role store iterfaces
/// </summary>
public class RoleStore : IQueryableRoleStore<ApplicationRole>
{
private RoleTable roleTable;
public Database Database { get; private set; }
public IQueryable<ApplicationRole> Roles
{
get
{
List<ApplicationRole> roles = roleTable.GetRoles();
return roles.AsQueryable();
}
}
public RoleStore()
{
Database = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
roleTable = new RoleTable(Database);
}
/// <summary>
/// Constructor that takes a MySQLDatabase as argument
/// </summary>
/// <param name="database"></param>
public RoleStore(Database database)
{
Database = database;
roleTable = new RoleTable(database);
}
public Task<IdentityResult> CreateAsync(ApplicationRole role, CancellationToken cancellationToken)
{
if (role == null)
{
throw new ArgumentNullException("role");
}
roleTable.Insert(role);
return Task.FromResult<IdentityResult>(IdentityResult.Success);
}
public Task<IdentityResult> DeleteAsync(ApplicationRole role, CancellationToken cancellationToken)
{
if (role == null)
{
throw new ArgumentNullException("user");
}
roleTable.Delete(role.Id);
return Task.FromResult<IdentityResult>(IdentityResult.Success);
}
public Task<ApplicationRole> FindByIdAsync(string roleId, CancellationToken cancellationToken)
{
ApplicationRole result = roleTable.GetRoleById(roleId) as ApplicationRole;
return Task.FromResult<ApplicationRole>(result);
}
public Task<bool> RoleExistsAsync(string roleId, CancellationToken cancellationToken)
{
ApplicationRole? result = roleTable.GetRoleById(roleId) as ApplicationRole;
if (result == null)
{
return Task.FromResult<bool>(false);
}
else
{
return Task.FromResult<bool>(true);
}
}
public Task<ApplicationRole?> FindByNameAsync(string roleName, CancellationToken cancellationToken)
{
ApplicationRole? result = roleTable.GetRoleByName(roleName) as ApplicationRole;
return Task.FromResult<ApplicationRole?>(result);
}
public Task<IdentityResult> UpdateAsync(ApplicationRole role, CancellationToken cancellationToken)
{
if (role == null)
{
throw new ArgumentNullException("user");
}
roleTable.Update(role);
return Task.FromResult<IdentityResult>(IdentityResult.Success);
}
public void Dispose()
{
if (Database != null)
{
Database = null;
}
}
public Task<string> GetRoleIdAsync(ApplicationRole role, CancellationToken cancellationToken)
{
if (role != null)
{
return Task.FromResult<string>(roleTable.GetRoleId(role.Name));
}
return Task.FromResult<string>(null);
}
public Task<string?> GetRoleNameAsync(ApplicationRole role, CancellationToken cancellationToken)
{
if (role != null)
{
return Task.FromResult<string?>(roleTable.GetRoleName(role.Id));
}
return Task.FromResult<string?>(null);
}
public Task SetRoleNameAsync(ApplicationRole role, string? roleName, CancellationToken cancellationToken)
{
if (role == null)
{
throw new ArgumentNullException("role");
}
role.Name = roleName;
roleTable.Update(role);
return Task.FromResult<IdentityResult>(IdentityResult.Success);
}
public Task<string?> GetNormalizedRoleNameAsync(ApplicationRole role, CancellationToken cancellationToken)
{
if (role != null)
{
return Task.FromResult<string?>(roleTable.GetRoleName(role.Id));
}
return Task.FromResult<string?>(null);
}
public Task SetNormalizedRoleNameAsync(ApplicationRole role, string? normalizedName, CancellationToken cancellationToken)
{
if (role == null)
{
throw new ArgumentNullException("role");
}
role.Name = normalizedName;
roleTable.Update(role);
return Task.FromResult<IdentityResult>(IdentityResult.Success);
}
}
}
@@ -0,0 +1,168 @@
using System;
using System.Collections.Generic;
using System.Data;
using gaseous_server.Classes;
using Microsoft.AspNetCore.Identity;
namespace Authentication
{
/// <summary>
/// Class that represents the Role table in the MySQL Database
/// </summary>
public class RoleTable
{
private Database _database;
/// <summary>
/// Constructor that takes a MySQLDatabase instance
/// </summary>
/// <param name="database"></param>
public RoleTable(Database database)
{
_database = database;
}
/// <summary>
/// Deltes a role from the Roles table
/// </summary>
/// <param name="roleId">The role Id</param>
/// <returns></returns>
public int Delete(string roleId)
{
string commandText = "Delete from Roles where Id = @id";
Dictionary<string, object> parameters = new Dictionary<string, object>();
parameters.Add("@id", roleId);
return (int)_database.ExecuteNonQuery(commandText, parameters);
}
/// <summary>
/// Inserts a new Role in the Roles table
/// </summary>
/// <param name="roleName">The role's name</param>
/// <returns></returns>
public int Insert(ApplicationRole role)
{
string commandText = "Insert into Roles (Id, Name) values (@id, @name)";
Dictionary<string, object> parameters = new Dictionary<string, object>();
parameters.Add("@name", role.Name);
parameters.Add("@id", role.Id);
return (int)_database.ExecuteNonQuery(commandText, parameters);
}
/// <summary>
/// Returns a role name given the roleId
/// </summary>
/// <param name="roleId">The role Id</param>
/// <returns>Role name</returns>
public string? GetRoleName(string roleId)
{
string commandText = "Select Name from Roles where Id = @id";
Dictionary<string, object> parameters = new Dictionary<string, object>();
parameters.Add("@id", roleId);
DataTable table = _database.ExecuteCMD(commandText, parameters);
if (table.Rows.Count == 0)
{
return null;
}
else
{
return (string)table.Rows[0][0];
}
}
/// <summary>
/// Returns the role Id given a role name
/// </summary>
/// <param name="roleName">Role's name</param>
/// <returns>Role's Id</returns>
public string? GetRoleId(string roleName)
{
string? roleId = null;
string commandText = "Select Id from Roles where Name = @name";
Dictionary<string, object> parameters = new Dictionary<string, object>() { { "@name", roleName } };
DataTable result = _database.ExecuteCMD(commandText, parameters);
if (result.Rows.Count > 0)
{
return Convert.ToString(result.Rows[0][0]);
}
return roleId;
}
/// <summary>
/// Gets the ApplicationRole given the role Id
/// </summary>
/// <param name="roleId"></param>
/// <returns></returns>
public ApplicationRole? GetRoleById(string roleId)
{
var roleName = GetRoleName(roleId);
ApplicationRole? role = null;
if(roleName != null)
{
role = new ApplicationRole();
role.Id = roleId;
role.Name = roleName;
role.NormalizedName = roleName.ToUpper();
}
return role;
}
/// <summary>
/// Gets the ApplicationRole given the role name
/// </summary>
/// <param name="roleName"></param>
/// <returns></returns>
public ApplicationRole? GetRoleByName(string roleName)
{
var roleId = GetRoleId(roleName);
ApplicationRole role = null;
if (roleId != null)
{
role = new ApplicationRole();
role.Id = roleId;
role.Name = roleName;
role.NormalizedName = roleName.ToUpper();
}
return role;
}
public int Update(ApplicationRole role)
{
string commandText = "Update Roles set Name = @name where Id = @id";
Dictionary<string, object> parameters = new Dictionary<string, object>();
parameters.Add("@id", role.Id);
return (int)_database.ExecuteNonQuery(commandText, parameters);
}
public List<ApplicationRole> GetRoles()
{
List<ApplicationRole> roles = new List<ApplicationRole>();
string commandText = "Select Name from Roles";
var rows = _database.ExecuteCMDDict(commandText);
foreach(Dictionary<string, object> row in rows)
{
ApplicationRole role = (ApplicationRole)Activator.CreateInstance(typeof(ApplicationRole));
role.Id = (string)row["Id"];
role.Name = (string)row["Name"];
role.NormalizedName = ((string)row["Name"]).ToUpper();
roles.Add(role);
}
return roles;
}
}
}
@@ -0,0 +1,95 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Security.Claims;
using gaseous_server.Classes;
using Microsoft.AspNetCore.Identity;
namespace Authentication
{
/// <summary>
/// Class that represents the UserClaims table in the MySQL Database
/// </summary>
public class UserClaimsTable
{
private Database _database;
/// <summary>
/// Constructor that takes a MySQLDatabase instance
/// </summary>
/// <param name="database"></param>
public UserClaimsTable(Database database)
{
_database = database;
}
/// <summary>
/// Returns a ClaimsIdentity instance given a userId
/// </summary>
/// <param name="userId">The user's id</param>
/// <returns></returns>
public ClaimsIdentity FindByUserId(string userId)
{
ClaimsIdentity claims = new ClaimsIdentity();
string commandText = "Select * from UserClaims where UserId = @userId";
Dictionary<string, object> parameters = new Dictionary<string, object>() { { "@UserId", userId } };
var rows = _database.ExecuteCMD(commandText, parameters).Rows;
foreach (DataRow row in rows)
{
Claim claim = new Claim((string)row["ClaimType"], (string)row["ClaimValue"]);
claims.AddClaim(claim);
}
return claims;
}
/// <summary>
/// Deletes all claims from a user given a userId
/// </summary>
/// <param name="userId">The user's id</param>
/// <returns></returns>
public int Delete(string userId)
{
string commandText = "Delete from UserClaims where UserId = @userId";
Dictionary<string, object> parameters = new Dictionary<string, object>();
parameters.Add("userId", userId);
return (int)_database.ExecuteNonQuery(commandText, parameters);
}
/// <summary>
/// Inserts a new claim in UserClaims table
/// </summary>
/// <param name="userClaim">User's claim to be added</param>
/// <param name="userId">User's id</param>
/// <returns></returns>
public int Insert(Claim userClaim, string userId)
{
string commandText = "Insert into UserClaims (ClaimValue, ClaimType, UserId) values (@value, @type, @userId)";
Dictionary<string, object> parameters = new Dictionary<string, object>();
parameters.Add("value", userClaim.Value);
parameters.Add("type", userClaim.Type);
parameters.Add("userId", userId);
return (int)_database.ExecuteNonQuery(commandText, parameters);
}
/// <summary>
/// Deletes a claim from a user
/// </summary>
/// <param name="user">The user to have a claim deleted</param>
/// <param name="claim">A claim to be deleted from user</param>
/// <returns></returns>
public int Delete(IdentityUser user, Claim claim)
{
string commandText = "Delete from UserClaims where UserId = @userId and @ClaimValue = @value and ClaimType = @type";
Dictionary<string, object> parameters = new Dictionary<string, object>();
parameters.Add("userId", user.Id);
parameters.Add("value", claim.Value);
parameters.Add("type", claim.Type);
return (int)_database.ExecuteNonQuery(commandText, parameters);
}
}
}
@@ -0,0 +1,117 @@
using gaseous_server.Classes;
using Microsoft.AspNetCore.Identity;
using System.Collections.Generic;
using System.Data;
namespace Authentication
{
/// <summary>
/// Class that represents the UserLogins table in the MySQL Database
/// </summary>
public class UserLoginsTable
{
private Database _database;
/// <summary>
/// Constructor that takes a MySQLDatabase instance
/// </summary>
/// <param name="database"></param>
public UserLoginsTable(Database database)
{
_database = database;
}
/// <summary>
/// Deletes a login from a user in the UserLogins table
/// </summary>
/// <param name="user">User to have login deleted</param>
/// <param name="login">Login to be deleted from user</param>
/// <returns></returns>
public int Delete(IdentityUser user, UserLoginInfo login)
{
string commandText = "Delete from UserLogins where UserId = @userId and LoginProvider = @loginProvider and ProviderKey = @providerKey";
Dictionary<string, object> parameters = new Dictionary<string, object>();
parameters.Add("UserId", user.Id);
parameters.Add("loginProvider", login.LoginProvider);
parameters.Add("providerKey", login.ProviderKey);
return (int)_database.ExecuteNonQuery(commandText, parameters);
}
/// <summary>
/// Deletes all Logins from a user in the UserLogins table
/// </summary>
/// <param name="userId">The user's id</param>
/// <returns></returns>
public int Delete(string userId)
{
string commandText = "Delete from UserLogins where UserId = @userId";
Dictionary<string, object> parameters = new Dictionary<string, object>();
parameters.Add("UserId", userId);
return (int)_database.ExecuteNonQuery(commandText, parameters);
}
/// <summary>
/// Inserts a new login in the UserLogins table
/// </summary>
/// <param name="user">User to have new login added</param>
/// <param name="login">Login to be added</param>
/// <returns></returns>
public int Insert(IdentityUser user, UserLoginInfo login)
{
string commandText = "Insert into UserLogins (LoginProvider, ProviderKey, UserId) values (@loginProvider, @providerKey, @userId)";
Dictionary<string, object> parameters = new Dictionary<string, object>();
parameters.Add("loginProvider", login.LoginProvider);
parameters.Add("providerKey", login.ProviderKey);
parameters.Add("userId", user.Id);
return (int)_database.ExecuteNonQuery(commandText, parameters);
}
/// <summary>
/// Return a userId given a user's login
/// </summary>
/// <param name="userLogin">The user's login info</param>
/// <returns></returns>
public string? FindUserIdByLogin(UserLoginInfo userLogin)
{
string commandText = "Select UserId from UserLogins where LoginProvider = @loginProvider and ProviderKey = @providerKey";
Dictionary<string, object> parameters = new Dictionary<string, object>();
parameters.Add("loginProvider", userLogin.LoginProvider);
parameters.Add("providerKey", userLogin.ProviderKey);
DataTable table = _database.ExecuteCMD(commandText, parameters);
if (table.Rows.Count == 0)
{
return null;
}
else
{
return (string)table.Rows[0][0];
}
}
/// <summary>
/// Returns a list of user's logins
/// </summary>
/// <param name="userId">The user's id</param>
/// <returns></returns>
public List<UserLoginInfo> FindByUserId(string userId)
{
List<UserLoginInfo> logins = new List<UserLoginInfo>();
string commandText = "Select * from UserLogins where UserId = @userId";
Dictionary<string, object> parameters = new Dictionary<string, object>() { { "@userId", userId } };
var rows = _database.ExecuteCMD(commandText, parameters).Rows;
foreach (DataRow row in rows)
{
var login = new UserLoginInfo((string)row["LoginProvider"], (string)row["ProviderKey"], (string)row["LoginProvider"]);
logins.Add(login);
}
return logins;
}
}
}
@@ -0,0 +1,86 @@
using System;
using System.Collections.Generic;
using System.Data;
using gaseous_server.Classes;
using Microsoft.AspNetCore.Identity;
namespace Authentication
{
/// <summary>
/// Class that represents the UserRoles table in the MySQL Database
/// </summary>
public class UserRolesTable
{
private Database _database;
/// <summary>
/// Constructor that takes a MySQLDatabase instance
/// </summary>
/// <param name="database"></param>
public UserRolesTable(Database database)
{
_database = database;
}
/// <summary>
/// Returns a list of user's roles
/// </summary>
/// <param name="userId">The user's id</param>
/// <returns></returns>
public List<string> FindByUserId(string userId)
{
List<string> roles = new List<string>();
string commandText = "Select Roles.Name from UserRoles, Roles where UserRoles.UserId = @userId and UserRoles.RoleId = Roles.Id";
Dictionary<string, object> parameters = new Dictionary<string, object>();
parameters.Add("@userId", userId);
var rows = _database.ExecuteCMD(commandText, parameters).Rows;
foreach(DataRow row in rows)
{
roles.Add((string)row["Name"]);
}
return roles;
}
/// <summary>
/// Deletes all roles from a user in the UserRoles table
/// </summary>
/// <param name="userId">The user's id</param>
/// <returns></returns>
public int Delete(string userId)
{
string commandText = "Delete from UserRoles where UserId = @userId";
Dictionary<string, object> parameters = new Dictionary<string, object>();
parameters.Add("UserId", userId);
return (int)_database.ExecuteNonQuery(commandText, parameters);
}
public int DeleteUserFromRole(string userId, string roleId)
{
string commandText = "Delete from UserRoles where UserId = @userId and RoleId = @roleId";
Dictionary<string, object> parameters = new Dictionary<string, object>();
parameters.Add("userId", userId);
parameters.Add("roleId", roleId);
return (int)_database.ExecuteNonQuery(commandText, parameters);
}
/// <summary>
/// Inserts a new role for a user in the UserRoles table
/// </summary>
/// <param name="user">The User</param>
/// <param name="roleId">The Role's id</param>
/// <returns></returns>
public int Insert(IdentityUser user, string roleId)
{
string commandText = "Insert into UserRoles (UserId, RoleId) values (@userId, @roleId)";
Dictionary<string, object> parameters = new Dictionary<string, object>();
parameters.Add("userId", user.Id);
parameters.Add("roleId", roleId);
return (int)_database.ExecuteNonQuery(commandText, parameters);
}
}
}
@@ -0,0 +1,616 @@
using System;
using System.Security.Claims;
using System.Threading.Tasks;
using gaseous_server.Classes;
using Microsoft.AspNetCore.Identity;
using MySqlConnector;
namespace Authentication
{
public class UserStore :
IUserStore<ApplicationUser>,
IUserRoleStore<ApplicationUser>,
IUserLoginStore<ApplicationUser>,
IUserClaimStore<ApplicationUser>,
IUserPasswordStore<ApplicationUser>,
IUserSecurityStampStore<ApplicationUser>,
IQueryableUserStore<ApplicationUser>,
IUserEmailStore<ApplicationUser>,
IUserPhoneNumberStore<ApplicationUser>,
IUserTwoFactorStore<ApplicationUser>,
IUserLockoutStore<ApplicationUser>
{
private Database database;
private UserTable<ApplicationUser> userTable;
private RoleTable roleTable;
private UserRolesTable userRolesTable;
private UserLoginsTable userLoginsTable;
private UserClaimsTable userClaimsTable;
public UserStore()
{
database = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
userTable = new UserTable<ApplicationUser>(database);
roleTable = new RoleTable(database);
userRolesTable = new UserRolesTable(database);
userLoginsTable = new UserLoginsTable(database);
userClaimsTable = new UserClaimsTable(database);
}
public UserStore(Database database)
{
this.database = database;
userTable = new UserTable<ApplicationUser>(database);
roleTable = new RoleTable(database);
userRolesTable = new UserRolesTable(database);
userLoginsTable = new UserLoginsTable(database);
userClaimsTable = new UserClaimsTable(database);
}
public IQueryable<ApplicationUser> Users
{
get
{
List<ApplicationUser> users = userTable.GetUsers();
return users.AsQueryable();
}
}
public Task AddClaimsAsync(ApplicationUser user, IEnumerable<Claim> claims, CancellationToken cancellationToken)
{
if (user == null)
{
throw new ArgumentNullException("user");
}
if (claims == null)
{
throw new ArgumentNullException("user");
}
foreach (Claim claim in claims)
{
userClaimsTable.Insert(claim, user.Id);
}
return Task.FromResult<object>(null);
}
public Task AddLoginAsync(ApplicationUser user, UserLoginInfo login, CancellationToken cancellationToken)
{
if (user == null)
{
throw new ArgumentNullException("user");
}
if (login == null)
{
throw new ArgumentNullException("login");
}
userLoginsTable.Insert(user, login);
return Task.FromResult<object>(null);
}
public Task AddToRoleAsync(ApplicationUser user, string roleName, CancellationToken cancellationToken)
{
if (user == null)
{
throw new ArgumentNullException("user");
}
if (string.IsNullOrEmpty(roleName))
{
throw new ArgumentException("Argument cannot be null or empty: roleName.");
}
string roleId = roleTable.GetRoleId(roleName);
if (!string.IsNullOrEmpty(roleId))
{
userRolesTable.Insert(user, roleId);
}
return Task.FromResult<object>(null);
}
public Task<IdentityResult> CreateAsync(ApplicationUser user, CancellationToken cancellationToken)
{
if (user == null)
{
throw new ArgumentNullException("user");
}
userTable.Insert(user);
return Task.FromResult<IdentityResult>(IdentityResult.Success);
}
public Task<IdentityResult> DeleteAsync(ApplicationUser user, CancellationToken cancellationToken)
{
if (user != null)
{
userTable.Delete(user);
}
return Task.FromResult<IdentityResult>(IdentityResult.Success);
}
public void Dispose()
{
if (database != null)
{
database = null;
}
}
public Task<ApplicationUser?> FindByEmailAsync(string normalizedEmail, CancellationToken cancellationToken)
{
if (String.IsNullOrEmpty(normalizedEmail))
{
throw new ArgumentNullException("email");
}
ApplicationUser result = userTable.GetUserByEmail(normalizedEmail) as ApplicationUser;
if (result != null)
{
return Task.FromResult<ApplicationUser>(result);
}
return Task.FromResult<ApplicationUser>(null);
}
public Task<ApplicationUser?> FindByIdAsync(string userId, CancellationToken cancellationToken)
{
if (string.IsNullOrEmpty(userId))
{
throw new ArgumentException("Null or empty argument: userId");
}
ApplicationUser result = userTable.GetUserById(userId) as ApplicationUser;
if (result != null)
{
return Task.FromResult<ApplicationUser>(result);
}
return Task.FromResult<ApplicationUser>(null);
}
public Task<ApplicationUser?> FindByLoginAsync(string loginProvider, string providerKey, CancellationToken cancellationToken)
{
if (loginProvider == null || providerKey == null)
{
throw new ArgumentNullException("login");
}
UserLoginInfo login = new UserLoginInfo(loginProvider, providerKey, loginProvider);
var userId = userLoginsTable.FindUserIdByLogin(login);
if (userId != null)
{
ApplicationUser user = userTable.GetUserById(userId) as ApplicationUser;
if (user != null)
{
return Task.FromResult<ApplicationUser>(user);
}
}
return Task.FromResult<ApplicationUser>(null);
}
public Task<ApplicationUser?> FindByNameAsync(string normalizedUserName, CancellationToken cancellationToken)
{
if (string.IsNullOrEmpty(normalizedUserName))
{
throw new ArgumentException("Null or empty argument: normalizedUserName");
}
List<ApplicationUser> result = userTable.GetUserByName(normalizedUserName) as List<ApplicationUser>;
// Should I throw if > 1 user?
if (result != null && result.Count == 1)
{
return Task.FromResult<ApplicationUser>(result[0]);
}
return Task.FromResult<ApplicationUser>(null);
}
public Task<int> GetAccessFailedCountAsync(ApplicationUser user, CancellationToken cancellationToken)
{
return Task.FromResult(user.AccessFailedCount);
}
public Task<IList<Claim>> GetClaimsAsync(ApplicationUser user, CancellationToken cancellationToken)
{
ClaimsIdentity identity = userClaimsTable.FindByUserId(user.Id);
return Task.FromResult<IList<Claim>>(identity.Claims.ToList());
}
public Task<string?> GetEmailAsync(ApplicationUser user, CancellationToken cancellationToken)
{
return Task.FromResult(user.Email);
}
public Task<bool> GetEmailConfirmedAsync(ApplicationUser user, CancellationToken cancellationToken)
{
return Task.FromResult(user.EmailConfirmed);
}
public Task<bool> GetLockoutEnabledAsync(ApplicationUser user, CancellationToken cancellationToken)
{
return Task.FromResult(user.LockoutEnabled);
}
public Task<DateTimeOffset?> GetLockoutEndDateAsync(ApplicationUser user, CancellationToken cancellationToken)
{
if (user.LockoutEnd.HasValue)
{
return Task.FromResult((DateTimeOffset?)user.LockoutEnd.Value);
}
else
{
return Task.FromResult((DateTimeOffset?)new DateTimeOffset());
}
}
public Task<IList<UserLoginInfo>> GetLoginsAsync(ApplicationUser user, CancellationToken cancellationToken)
{
if (user == null)
{
throw new ArgumentNullException("user");
}
List<UserLoginInfo> logins = userLoginsTable.FindByUserId(user.Id);
if (logins != null)
{
return Task.FromResult<IList<UserLoginInfo>>(logins);
}
return Task.FromResult<IList<UserLoginInfo>>(null);
}
public Task<string?> GetNormalizedEmailAsync(ApplicationUser user, CancellationToken cancellationToken)
{
return Task.FromResult(user.NormalizedEmail);
}
public Task<string?> GetNormalizedUserNameAsync(ApplicationUser user, CancellationToken cancellationToken)
{
if (user != null)
{
return Task.FromResult<string?>(userTable.GetUserName(user.Id));
}
return Task.FromResult<string?>(null);
}
public Task<string?> GetPasswordHashAsync(ApplicationUser user, CancellationToken cancellationToken)
{
if (user != null)
{
return Task.FromResult<string?>(userTable.GetPasswordHash(user.Id));
}
return Task.FromResult<string?>(null);
}
public Task<string?> GetPhoneNumberAsync(ApplicationUser user, CancellationToken cancellationToken)
{
return Task.FromResult(user.PhoneNumber);
}
public Task<bool> GetPhoneNumberConfirmedAsync(ApplicationUser user, CancellationToken cancellationToken)
{
return Task.FromResult(user.PhoneNumberConfirmed);
}
public Task<IList<string>> GetRolesAsync(ApplicationUser user, CancellationToken cancellationToken)
{
if (user == null)
{
throw new ArgumentNullException("user");
}
List<string> roles = userRolesTable.FindByUserId(user.Id);
{
if (roles != null)
{
return Task.FromResult<IList<string>>(roles);
}
}
return Task.FromResult<IList<string>>(null);
}
public Task<string?> GetSecurityStampAsync(ApplicationUser user, CancellationToken cancellationToken)
{
return Task.FromResult(user.SecurityStamp);
}
public Task<bool> GetTwoFactorEnabledAsync(ApplicationUser user, CancellationToken cancellationToken)
{
return Task.FromResult(user.TwoFactorEnabled);
}
public Task<string> GetUserIdAsync(ApplicationUser user, CancellationToken cancellationToken)
{
if (user != null)
{
return Task.FromResult<string>(userTable.GetUserId(user.NormalizedUserName));
}
return Task.FromResult<string>(null);
}
public Task<string?> GetUserNameAsync(ApplicationUser user, CancellationToken cancellationToken)
{
if (user != null)
{
//return Task.FromResult<string?>(userTable.GetUserName(user.Id));
return Task.FromResult(user.UserName);
}
return Task.FromResult<string?>(null);
}
public Task<IList<ApplicationUser>> GetUsersForClaimAsync(Claim claim, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
public Task<IList<ApplicationUser>> GetUsersInRoleAsync(string roleName, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
public Task<bool> HasPasswordAsync(ApplicationUser user, CancellationToken cancellationToken)
{
var hasPassword = !string.IsNullOrEmpty(userTable.GetPasswordHash(user.Id));
return Task.FromResult<bool>(Boolean.Parse(hasPassword.ToString()));
}
public Task<int> IncrementAccessFailedCountAsync(ApplicationUser user, CancellationToken cancellationToken)
{
user.AccessFailedCount++;
userTable.Update(user);
return Task.FromResult(user.AccessFailedCount);
}
public Task<bool> IsInRoleAsync(ApplicationUser user, string roleName, CancellationToken cancellationToken)
{
if (user == null)
{
throw new ArgumentNullException("user");
}
if (string.IsNullOrEmpty(roleName))
{
throw new ArgumentNullException("role");
}
List<string> roles = userRolesTable.FindByUserId(user.Id);
{
if (roles != null)
{
foreach (string role in roles)
{
if (role.ToUpper() == roleName.ToUpper())
{
return Task.FromResult<bool>(true);
}
}
}
}
return Task.FromResult<bool>(false);
}
public Task RemoveClaimsAsync(ApplicationUser user, IEnumerable<Claim> claims, CancellationToken cancellationToken)
{
if (user == null)
{
throw new ArgumentNullException("user");
}
if (claims == null)
{
throw new ArgumentNullException("claim");
}
foreach (Claim claim in claims)
{
userClaimsTable.Delete(user, claim);
}
return Task.FromResult<object>(null);
}
public Task RemoveFromRoleAsync(ApplicationUser user, string roleName, CancellationToken cancellationToken)
{
if (user == null)
{
throw new ArgumentNullException("user");
}
if (roleName == null)
{
throw new ArgumentNullException("role");
}
IdentityRole? role = roleTable.GetRoleByName(roleName);
if (role != null)
{
userRolesTable.DeleteUserFromRole(user.Id, role.Id);
}
return Task.FromResult<Object>(null);
}
public Task RemoveLoginAsync(ApplicationUser user, string loginProvider, string providerKey, CancellationToken cancellationToken)
{
if (user == null)
{
throw new ArgumentNullException("user");
}
if (loginProvider == null || providerKey == null)
{
throw new ArgumentNullException("login");
}
UserLoginInfo login = new UserLoginInfo(loginProvider, providerKey, loginProvider);
userLoginsTable.Delete(user, login);
return Task.FromResult<Object>(null);
}
public Task ReplaceClaimAsync(ApplicationUser user, Claim claim, Claim newClaim, CancellationToken cancellationToken)
{
if (user == null)
{
throw new ArgumentNullException("user");
}
if (claim == null || newClaim == null)
{
throw new ArgumentNullException("claim");
}
userClaimsTable.Delete(user, claim);
userClaimsTable.Insert(newClaim, user.Id);
return Task.FromResult<Object>(null);
}
public Task ResetAccessFailedCountAsync(ApplicationUser user, CancellationToken cancellationToken)
{
user.AccessFailedCount = 0;
userTable.Update(user);
return Task.FromResult(0);
}
public Task SetEmailAsync(ApplicationUser user, string? email, CancellationToken cancellationToken)
{
user.Email = email;
userTable.Update(user);
return Task.FromResult(0);
}
public Task SetEmailConfirmedAsync(ApplicationUser user, bool confirmed, CancellationToken cancellationToken)
{
user.EmailConfirmed = confirmed;
userTable.Update(user);
return Task.FromResult(0);
}
public Task SetLockoutEnabledAsync(ApplicationUser user, bool enabled, CancellationToken cancellationToken)
{
user.LockoutEnabled = enabled;
userTable.Update(user);
return Task.FromResult(0);
}
public Task SetLockoutEndDateAsync(ApplicationUser user, DateTimeOffset? lockoutEnd, CancellationToken cancellationToken)
{
user.LockoutEnd = lockoutEnd;
userTable.Update(user);
return Task.FromResult(0);
}
public Task SetNormalizedEmailAsync(ApplicationUser user, string? normalizedEmail, CancellationToken cancellationToken)
{
user.NormalizedEmail = normalizedEmail;
userTable.Update(user);
return Task.FromResult(0);
}
public Task SetNormalizedUserNameAsync(ApplicationUser user, string? normalizedName, CancellationToken cancellationToken)
{
if (user == null)
{
throw new ArgumentNullException("user");
}
user.NormalizedUserName = normalizedName;
userTable.Update(user);
return Task.FromResult<IdentityResult>(IdentityResult.Success);
}
public Task SetPasswordHashAsync(ApplicationUser user, string? passwordHash, CancellationToken cancellationToken)
{
user.PasswordHash = passwordHash;
return Task.FromResult<Object>(null);
}
public Task SetPhoneNumberAsync(ApplicationUser user, string? phoneNumber, CancellationToken cancellationToken)
{
user.PhoneNumber = phoneNumber;
userTable.Update(user);
return Task.FromResult(0);
}
public Task SetPhoneNumberConfirmedAsync(ApplicationUser user, bool confirmed, CancellationToken cancellationToken)
{
user.PhoneNumberConfirmed = confirmed;
userTable.Update(user);
return Task.FromResult(0);
}
public Task SetSecurityStampAsync(ApplicationUser user, string stamp, CancellationToken cancellationToken)
{
user.SecurityStamp = stamp;
return Task.FromResult(0);
}
public Task SetTwoFactorEnabledAsync(ApplicationUser user, bool enabled, CancellationToken cancellationToken)
{
user.TwoFactorEnabled = enabled;
userTable.Update(user);
return Task.FromResult(0);
}
public Task SetUserNameAsync(ApplicationUser user, string? userName, CancellationToken cancellationToken)
{
if (user == null)
{
throw new ArgumentNullException("user");
}
user.UserName = userName;
userTable.Update(user);
return Task.FromResult<IdentityResult>(IdentityResult.Success);
}
public Task<IdentityResult> UpdateAsync(ApplicationUser user, CancellationToken cancellationToken)
{
if (user == null)
{
throw new ArgumentNullException("user");
}
userTable.Update(user);
return Task.FromResult<IdentityResult>(IdentityResult.Success);
}
}
}
@@ -0,0 +1,371 @@
using System;
using System.Collections.Generic;
using System.Data;
using gaseous_server.Classes;
using Microsoft.AspNetCore.Identity;
namespace Authentication
{
/// <summary>
/// Class that represents the Users table in the MySQL Database
/// </summary>
public class UserTable<TUser>
where TUser :ApplicationUser
{
private Database _database;
/// <summary>
/// Constructor that takes a MySQLDatabase instance
/// </summary>
/// <param name="database"></param>
public UserTable(Database database)
{
_database = database;
}
/// <summary>
/// Returns the user's name given a user id
/// </summary>
/// <param name="userId"></param>
/// <returns></returns>
public string? GetUserName(string userId)
{
string commandText = "Select NormalizedUserName from Users where Id = @id";
Dictionary<string, object> parameters = new Dictionary<string, object>() { { "@id", userId } };
DataTable table = _database.ExecuteCMD(commandText, parameters);
if (table.Rows.Count == 0)
{
return null;
}
else
{
return (string)table.Rows[0][0];
}
}
/// <summary>
/// Returns a User ID given a user name
/// </summary>
/// <param name="userName">The user's name</param>
/// <returns></returns>
public string? GetUserId(string normalizedUserName)
{
string commandText = "Select Id from Users where NormalizedUserName = @name";
Dictionary<string, object> parameters = new Dictionary<string, object>() { { "@name", normalizedUserName } };
DataTable table = _database.ExecuteCMD(commandText, parameters);
if (table.Rows.Count == 0)
{
return null;
}
else
{
return (string)table.Rows[0][0];
}
}
/// <summary>
/// Returns an TUser given the user's id
/// </summary>
/// <param name="userId">The user's id</param>
/// <returns></returns>
public TUser GetUserById(string userId)
{
TUser user = null;
string commandText = "Select * from Users where Id = @id";
Dictionary<string, object> parameters = new Dictionary<string, object>() { { "@id", userId } };
var rows = _database.ExecuteCMDDict(commandText, parameters);
if (rows != null && rows.Count == 1)
{
Dictionary<string, object> row = rows[0];
user = (TUser)Activator.CreateInstance(typeof(TUser));
user.Id = (string)row["Id"];
user.UserName = (string?)row["UserName"];
user.PasswordHash = (string?)(string.IsNullOrEmpty((string?)row["PasswordHash"]) ? null : row["PasswordHash"]);
user.SecurityStamp = (string?)(string.IsNullOrEmpty((string?)row["SecurityStamp"]) ? null : row["SecurityStamp"]);
user.ConcurrencyStamp = (string?)(string.IsNullOrEmpty((string?)row["ConcurrencyStamp"]) ? null : row["ConcurrencyStamp"]);
user.Email = (string?)(string.IsNullOrEmpty((string?)row["Email"]) ? null : row["Email"]);
user.EmailConfirmed = row["EmailConfirmed"] == "1" ? true:false;
user.PhoneNumber = (string?)(string.IsNullOrEmpty((string?)row["PhoneNumber"]) ? null : row["PhoneNumber"]);
user.PhoneNumberConfirmed = row["PhoneNumberConfirmed"] == "1" ? true : false;
user.NormalizedEmail = (string?)(string.IsNullOrEmpty((string?)row["NormalizedEmail"]) ? null : row["NormalizedEmail"]);
user.NormalizedUserName = (string?)(string.IsNullOrEmpty((string?)row["NormalizedUserName"]) ? null : row["NormalizedUserName"]);
user.LockoutEnabled = row["LockoutEnabled"] == "1" ? true : false;
user.LockoutEnd = string.IsNullOrEmpty((string?)row["LockoutEnd"]) ? DateTime.Now : DateTime.Parse((string?)row["LockoutEnd"]);
user.AccessFailedCount = string.IsNullOrEmpty((string?)row["AccessFailedCount"]) ? 0 : int.Parse((string?)row["AccessFailedCount"]);
user.TwoFactorEnabled = row["TwoFactorEnabled"] == "1" ? true:false;
user.SecurityProfile = GetSecurityProfile(user);
}
return user;
}
/// <summary>
/// Returns a list of TUser instances given a user name
/// </summary>
/// <param name="normalizedUserName">User's name</param>
/// <returns></returns>
public List<TUser> GetUserByName(string normalizedUserName)
{
List<TUser> users = new List<TUser>();
string commandText = "Select * from Users where NormalizedEmail = @name";
Dictionary<string, object> parameters = new Dictionary<string, object>() { { "@name", normalizedUserName } };
var rows = _database.ExecuteCMDDict(commandText, parameters);
foreach(Dictionary<string, object> row in rows)
{
TUser user = (TUser)Activator.CreateInstance(typeof(TUser));
user.Id = (string)row["Id"];
user.UserName = (string?)row["UserName"];
user.PasswordHash = (string?)(string.IsNullOrEmpty((string?)row["PasswordHash"]) ? null : row["PasswordHash"]);
user.SecurityStamp = (string?)(string.IsNullOrEmpty((string?)row["SecurityStamp"]) ? null : row["SecurityStamp"]);
user.ConcurrencyStamp = (string?)(string.IsNullOrEmpty((string?)row["ConcurrencyStamp"]) ? null : row["ConcurrencyStamp"]);
user.Email = (string?)(string.IsNullOrEmpty((string?)row["Email"]) ? null : row["Email"]);
user.EmailConfirmed = row["EmailConfirmed"] == "1" ? true:false;
user.PhoneNumber = (string?)(string.IsNullOrEmpty((string?)row["PhoneNumber"]) ? null : row["PhoneNumber"]);
user.PhoneNumberConfirmed = row["PhoneNumberConfirmed"] == "1" ? true : false;
user.NormalizedEmail = (string?)(string.IsNullOrEmpty((string?)row["NormalizedEmail"]) ? null : row["NormalizedEmail"]);
user.NormalizedUserName = (string?)(string.IsNullOrEmpty((string?)row["NormalizedUserName"]) ? null : row["NormalizedUserName"]);
user.LockoutEnabled = row["LockoutEnabled"] == "1" ? true : false;
user.LockoutEnd = string.IsNullOrEmpty((string?)row["LockoutEnd"]) ? DateTime.Now : DateTime.Parse((string?)row["LockoutEnd"]);
user.AccessFailedCount = string.IsNullOrEmpty((string?)row["AccessFailedCount"]) ? 0 : int.Parse((string?)row["AccessFailedCount"]);
user.TwoFactorEnabled = row["TwoFactorEnabled"] == "1" ? true:false;
user.SecurityProfile = GetSecurityProfile(user);
users.Add(user);
}
return users;
}
public List<TUser> GetUsers()
{
List<TUser> users = new List<TUser>();
string commandText = "Select * from Users order by NormalizedUserName";
var rows = _database.ExecuteCMDDict(commandText);
foreach(Dictionary<string, object> row in rows)
{
TUser user = (TUser)Activator.CreateInstance(typeof(TUser));
user.Id = (string)row["Id"];
user.UserName = (string?)row["UserName"];
user.PasswordHash = (string?)(string.IsNullOrEmpty((string?)row["PasswordHash"]) ? null : row["PasswordHash"]);
user.SecurityStamp = (string?)(string.IsNullOrEmpty((string?)row["SecurityStamp"]) ? null : row["SecurityStamp"]);
user.ConcurrencyStamp = (string?)(string.IsNullOrEmpty((string?)row["ConcurrencyStamp"]) ? null : row["ConcurrencyStamp"]);
user.Email = (string?)(string.IsNullOrEmpty((string?)row["Email"]) ? null : row["Email"]);
user.EmailConfirmed = row["EmailConfirmed"] == "1" ? true:false;
user.PhoneNumber = (string?)(string.IsNullOrEmpty((string?)row["PhoneNumber"]) ? null : row["PhoneNumber"]);
user.PhoneNumberConfirmed = row["PhoneNumberConfirmed"] == "1" ? true : false;
user.NormalizedEmail = (string?)(string.IsNullOrEmpty((string?)row["NormalizedEmail"]) ? null : row["NormalizedEmail"]);
user.NormalizedUserName = (string?)(string.IsNullOrEmpty((string?)row["NormalizedUserName"]) ? null : row["NormalizedUserName"]);
user.LockoutEnabled = row["LockoutEnabled"] == "1" ? true : false;
user.LockoutEnd = string.IsNullOrEmpty((string?)row["LockoutEnd"]) ? DateTime.Now : DateTime.Parse((string?)row["LockoutEnd"]);
user.AccessFailedCount = string.IsNullOrEmpty((string?)row["AccessFailedCount"]) ? 0 : int.Parse((string?)row["AccessFailedCount"]);
user.TwoFactorEnabled = row["TwoFactorEnabled"] == "1" ? true:false;
user.SecurityProfile = GetSecurityProfile(user);
users.Add(user);
}
return users;
}
public TUser GetUserByEmail(string email)
{
List<TUser> users = GetUserByName(email);
if (users.Count == 0)
{
return null;
}
else
{
return users[0];
}
}
/// <summary>
/// Return the user's password hash
/// </summary>
/// <param name="userId">The user's id</param>
/// <returns></returns>
public string GetPasswordHash(string userId)
{
string commandText = "Select PasswordHash from Users where Id = @id";
Dictionary<string, object> parameters = new Dictionary<string, object>();
parameters.Add("@id", userId);
DataTable table = _database.ExecuteCMD(commandText, parameters);
if (table.Rows.Count == 0)
{
return null;
}
else
{
return (string)table.Rows[0][0];
}
}
/// <summary>
/// Sets the user's password hash
/// </summary>
/// <param name="userId"></param>
/// <param name="passwordHash"></param>
/// <returns></returns>
public int SetPasswordHash(string userId, string passwordHash)
{
string commandText = "Update Users set PasswordHash = @pwdHash where Id = @id";
Dictionary<string, object> parameters = new Dictionary<string, object>();
parameters.Add("@pwdHash", passwordHash);
parameters.Add("@id", userId);
return _database.ExecuteCMD(commandText, parameters).Rows.Count;
}
/// <summary>
/// Returns the user's security stamp
/// </summary>
/// <param name="userId"></param>
/// <returns></returns>
public string GetSecurityStamp(string userId)
{
string commandText = "Select SecurityStamp from Users where Id = @id";
Dictionary<string, object> parameters = new Dictionary<string, object>() { { "@id", userId } };
DataTable table = _database.ExecuteCMD(commandText, parameters);
if (table.Rows.Count == 0)
{
return null;
}
else
{
return (string)table.Rows[0][0];
}
}
/// <summary>
/// Inserts a new user in the Users table
/// </summary>
/// <param name="user"></param>
/// <returns></returns>
public int Insert(TUser user)
{
string commandText = @"Insert into Users (UserName, Id, PasswordHash, SecurityStamp, ConcurrencyStamp, Email, EmailConfirmed, PhoneNumber, PhoneNumberConfirmed, NormalizedEmail, NormalizedUserName, AccessFailedCount, LockoutEnabled, LockoutEnd, TwoFactorEnabled) values (@name, @id, @pwdHash, @SecStamp, @concurrencystamp, @email ,@emailconfirmed ,@phonenumber, @phonenumberconfirmed, @normalizedemail, @normalizedusername, @accesscount, @lockoutenabled, @lockoutenddate, @twofactorenabled);";
Dictionary<string, object> parameters = new Dictionary<string, object>();
parameters.Add("@name", user.UserName);
parameters.Add("@id", user.Id);
parameters.Add("@pwdHash", user.PasswordHash);
parameters.Add("@SecStamp", user.SecurityStamp);
parameters.Add("@concurrencystamp", user.ConcurrencyStamp);
parameters.Add("@email", user.Email);
parameters.Add("@emailconfirmed", user.EmailConfirmed);
parameters.Add("@phonenumber", user.PhoneNumber);
parameters.Add("@phonenumberconfirmed", user.PhoneNumberConfirmed);
parameters.Add("@normalizedemail", user.NormalizedEmail);
parameters.Add("@normalizedusername", user.NormalizedUserName);
parameters.Add("@accesscount", user.AccessFailedCount);
parameters.Add("@lockoutenabled", user.LockoutEnabled);
parameters.Add("@lockoutenddate", user.LockoutEnd);
parameters.Add("@twofactorenabled", user.TwoFactorEnabled);
// set default security profile
SetSecurityProfile(user, new SecurityProfileViewModel());
return _database.ExecuteCMD(commandText, parameters).Rows.Count;
}
/// <summary>
/// Deletes a user from the Users table
/// </summary>
/// <param name="userId">The user's id</param>
/// <returns></returns>
private int Delete(string userId)
{
string commandText = "Delete from Users where Id = @userId";
Dictionary<string, object> parameters = new Dictionary<string, object>();
parameters.Add("@userId", userId);
return _database.ExecuteCMD(commandText, parameters).Rows.Count;
}
/// <summary>
/// Deletes a user from the Users table
/// </summary>
/// <param name="user"></param>
/// <returns></returns>
public int Delete(TUser user)
{
return Delete(user.Id);
}
/// <summary>
/// Updates a user in the Users table
/// </summary>
/// <param name="user"></param>
/// <returns></returns>
public int Update(TUser user)
{
string commandText = @"Update Users set UserName = @userName, PasswordHash = @pwdHash, SecurityStamp = @secStamp, ConcurrencyStamp = @concurrencystamp, Email = @email, EmailConfirmed = @emailconfirmed, PhoneNumber = @phonenumber, PhoneNumberConfirmed = @phonenumberconfirmed, NormalizedEmail = @normalizedemail, NormalizedUserName = @normalizedusername, AccessFailedCount = @accesscount, LockoutEnabled = @lockoutenabled, LockoutEnd = @lockoutenddate, TwoFactorEnabled=@twofactorenabled WHERE Id = @userId;";
Dictionary<string, object> parameters = new Dictionary<string, object>();
parameters.Add("@userId", user.Id);
parameters.Add("@userName", user.UserName);
parameters.Add("@pwdHash", user.PasswordHash);
parameters.Add("@SecStamp", user.SecurityStamp);
parameters.Add("@concurrencystamp", user.ConcurrencyStamp);
parameters.Add("@email", user.Email);
parameters.Add("@emailconfirmed", user.EmailConfirmed);
parameters.Add("@phonenumber", user.PhoneNumber);
parameters.Add("@phonenumberconfirmed", user.PhoneNumberConfirmed);
parameters.Add("@normalizedemail", user.NormalizedEmail);
parameters.Add("@normalizedusername", user.NormalizedUserName);
parameters.Add("@accesscount", user.AccessFailedCount);
parameters.Add("@lockoutenabled", user.LockoutEnabled);
parameters.Add("@lockoutenddate", user.LockoutEnd);
parameters.Add("@twofactorenabled", user.TwoFactorEnabled);
// set the security profile
SetSecurityProfile(user, user.SecurityProfile);
return _database.ExecuteCMD(commandText, parameters).Rows.Count;
}
private SecurityProfileViewModel GetSecurityProfile(TUser user)
{
string sql = "SELECT SecurityProfile FROM users WHERE Id=@Id;";
Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("Id", user.Id);
List<Dictionary<string, object>> data = _database.ExecuteCMDDict(sql, dbDict);
if (data.Count == 0)
{
// no saved profile - return the default one
return new SecurityProfileViewModel();
}
else
{
string? securityProfileString = (string?)data[0]["SecurityProfile"];
if (securityProfileString != null && securityProfileString != "null")
{
SecurityProfileViewModel securityProfile = Newtonsoft.Json.JsonConvert.DeserializeObject<SecurityProfileViewModel>(securityProfileString);
return securityProfile;
}
else
{
return new SecurityProfileViewModel();
}
}
}
private int SetSecurityProfile(TUser user, SecurityProfileViewModel securityProfile)
{
string commandText = "UPDATE users SET SecurityProfile=@SecurityProfile WHERE Id=@Id;";
Dictionary<string, object> parameters = new Dictionary<string, object>();
parameters.Add("Id", user.Id);
parameters.Add("SecurityProfile", Newtonsoft.Json.JsonConvert.SerializeObject(securityProfile));
return _database.ExecuteCMD(commandText, parameters).Rows.Count;
}
}
}
@@ -0,0 +1,100 @@
using System.ComponentModel.DataAnnotations;
namespace Authentication
{
public class ExternalLoginConfirmationViewModel
{
[Required]
[EmailAddress]
[Display(Name = "Email")]
public string Email { get; set; }
}
public class ManageUserViewModel
{
[Required]
[DataType(DataType.Password)]
[Display(Name = "Current password")]
public string OldPassword { get; set; }
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "New password")]
public string NewPassword { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm new password")]
[Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
}
public class LoginViewModel
{
[Required]
[EmailAddress]
[Display(Name = "Email")]
public string Email { get; set; }
[Required]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[Display(Name = "Remember me?")]
public bool RememberMe { get; set; }
}
public class RegisterViewModel
{
[Required]
[DataType(DataType.Text)]
[Display(Name = "User name")]
public string UserName { get; set; }
[Required]
[EmailAddress]
[Display(Name = "Email")]
public string Email { get; set; }
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm password")]
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
}
public class ResetPasswordViewModel
{
[Required]
[EmailAddress]
[Display(Name = "Email")]
public string Email { get; set; }
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm password")]
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
public string Code { get; set; }
}
public class ForgotPasswordViewModel
{
[Required]
[EmailAddress]
[Display(Name = "Email")]
public string Email { get; set; }
}
}
@@ -0,0 +1,14 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.ComponentModel.DataAnnotations;
namespace Authentication;
public class AddPhoneNumberViewModel
{
[Required]
[Phone]
[Display(Name = "Phone number")]
public string PhoneNumber { get; set; }
}
@@ -0,0 +1,25 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.ComponentModel.DataAnnotations;
namespace Authentication;
public class ChangePasswordViewModel
{
[Required]
[DataType(DataType.Password)]
[Display(Name = "Current password")]
public string OldPassword { get; set; }
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 10)]
[DataType(DataType.Password)]
[Display(Name = "New password")]
public string NewPassword { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm new password")]
[Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
}
@@ -0,0 +1,13 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.ComponentModel.DataAnnotations;
namespace Authentication;
public class DisplayRecoveryCodesViewModel
{
[Required]
public IEnumerable<string> Codes { get; set; }
}
@@ -0,0 +1,21 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using Microsoft.AspNetCore.Identity;
namespace Authentication;
public class IndexViewModel
{
public bool HasPassword { get; set; }
public IList<UserLoginInfo> Logins { get; set; }
public string PhoneNumber { get; set; }
public bool TwoFactor { get; set; }
public bool BrowserRemembered { get; set; }
public string AuthenticatorKey { get; set; }
}
@@ -0,0 +1,14 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Identity;
namespace Authentication;
public class ManageLoginsViewModel
{
public IList<UserLoginInfo> CurrentLogins { get; set; }
public IList<AuthenticationScheme> OtherLogins { get; set; }
}
@@ -0,0 +1,46 @@
namespace Authentication
{
public class ProfileBasicViewModel
{
public string UserId { get; set; }
public string UserName { get; set; }
public string EmailAddress { get; set; }
public List<String> Roles { get; set; }
public SecurityProfileViewModel SecurityProfile { get; set; }
public string HighestRole {
get
{
string _highestRole = "";
foreach (string role in Roles)
{
switch (role)
{
case "Admin":
// there is no higher
_highestRole = role;
break;
case "Gamer":
// only one high is Admin, so check for that
if (_highestRole != "Admin")
{
_highestRole = role;
}
break;
case "Player":
// make sure _highestRole isn't already set to Gamer or Admin
if (_highestRole != "Admin" && _highestRole != "Gamer")
{
_highestRole = role;
}
break;
default:
_highestRole = "Player";
break;
}
}
return _highestRole;
}
}
}
}
@@ -0,0 +1,10 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
namespace Authentication;
public class RemoveLoginViewModel
{
public string LoginProvider { get; set; }
public string ProviderKey { get; set; }
}
@@ -0,0 +1,18 @@
using System.ComponentModel.DataAnnotations;
namespace Authentication
{
public class SecurityProfileViewModel
{
public AgeRestrictionItem AgeRestrictionPolicy { get; set; } = new AgeRestrictionItem{
MaximumAgeRestriction = "Adult",
IncludeUnrated = true
};
public class AgeRestrictionItem
{
public string MaximumAgeRestriction { get; set; }
public bool IncludeUnrated { get; set; }
}
}
}
@@ -0,0 +1,17 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using Microsoft.AspNetCore.Mvc.Rendering;
namespace Authentication;
public class SendCodeViewModel
{
public string SelectedProvider { get; set; }
public ICollection<SelectListItem> Providers { get; set; }
public string ReturnUrl { get; set; }
public bool RememberMe { get; set; }
}
@@ -0,0 +1,20 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.ComponentModel.DataAnnotations;
namespace Authentication;
public class SetPasswordViewModel
{
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "New password")]
public string NewPassword { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm new password")]
[Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
}
@@ -0,0 +1,14 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.ComponentModel.DataAnnotations;
namespace Authentication;
public class UseRecoveryCodeViewModel
{
[Required]
public string Code { get; set; }
public string ReturnUrl { get; set; }
}
@@ -0,0 +1,47 @@
namespace Authentication
{
public class UserViewModel
{
public string Id { get; set; }
public string EmailAddress { get; set; }
public bool LockoutEnabled { get; set; }
public DateTimeOffset? LockoutEnd { get; set; }
public List<string> Roles { get; set; }
public SecurityProfileViewModel SecurityProfile { get; set; }
public string HighestRole {
get
{
string _highestRole = "";
foreach (string role in Roles)
{
switch (role)
{
case "Admin":
// there is no higher
_highestRole = role;
break;
case "Gamer":
// only one high is Admin, so check for that
if (_highestRole != "Admin")
{
_highestRole = role;
}
break;
case "Player":
// make sure _highestRole isn't already set to Gamer or Admin
if (_highestRole != "Admin" && _highestRole != "Gamer")
{
_highestRole = role;
}
break;
default:
_highestRole = "Player";
break;
}
}
return _highestRole;
}
}
}
}
@@ -0,0 +1,20 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.ComponentModel.DataAnnotations;
namespace Authentication;
public class VerifyAuthenticatorCodeViewModel
{
[Required]
public string Code { get; set; }
public string ReturnUrl { get; set; }
[Display(Name = "Remember this browser?")]
public bool RememberBrowser { get; set; }
[Display(Name = "Remember me?")]
public bool RememberMe { get; set; }
}
@@ -0,0 +1,23 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.ComponentModel.DataAnnotations;
namespace Authentication;
public class VerifyCodeViewModel
{
[Required]
public string Provider { get; set; }
[Required]
public string Code { get; set; }
public string ReturnUrl { get; set; }
[Display(Name = "Remember this browser?")]
public bool RememberBrowser { get; set; }
[Display(Name = "Remember me?")]
public bool RememberMe { get; set; }
}
@@ -0,0 +1,17 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.ComponentModel.DataAnnotations;
namespace Authentication;
public class VerifyPhoneNumberViewModel
{
[Required]
public string Code { get; set; }
[Required]
[Phone]
[Display(Name = "Phone number")]
public string PhoneNumber { get; set; }
}
+114
View File
@@ -147,6 +147,50 @@ namespace gaseous_server.Classes
return _ExecuteCMD(Command, Parameters, Timeout, ConnectionString);
}
public List<Dictionary<string, object>> ExecuteCMDDict(string Command)
{
Dictionary<string, object> dbDict = new Dictionary<string, object>();
return _ExecuteCMDDict(Command, dbDict, 30, "");
}
public List<Dictionary<string, object>> ExecuteCMDDict(string Command, Dictionary<string, object> Parameters)
{
return _ExecuteCMDDict(Command, Parameters, 30, "");
}
public List<Dictionary<string, object>> ExecuteCMDDict(string Command, Dictionary<string, object> Parameters, int Timeout = 30, string ConnectionString = "")
{
return _ExecuteCMDDict(Command, Parameters, Timeout, ConnectionString);
}
private List<Dictionary<string, object>> _ExecuteCMDDict(string Command, Dictionary<string, object> Parameters, int Timeout = 30, string ConnectionString = "")
{
DataTable dataTable = _ExecuteCMD(Command, Parameters, Timeout, ConnectionString);
// convert datatable to dictionary
List<Dictionary<string, object?>> rows = new List<Dictionary<string, object?>>();
foreach (DataRow dataRow in dataTable.Rows)
{
Dictionary<string, object?> row = new Dictionary<string, object?>();
for (int i = 0; i < dataRow.Table.Columns.Count; i++)
{
string columnName = dataRow.Table.Columns[i].ColumnName;
if (dataRow[i] == System.DBNull.Value)
{
row.Add(columnName, null);
}
else
{
row.Add(columnName, dataRow[i].ToString());
}
}
rows.Add(row);
}
return rows;
}
private DataTable _ExecuteCMD(string Command, Dictionary<string, object> Parameters, int Timeout = 30, string ConnectionString = "")
{
if (ConnectionString == "") { ConnectionString = _ConnectionString; }
@@ -160,6 +204,35 @@ namespace gaseous_server.Classes
}
}
public int ExecuteNonQuery(string Command)
{
Dictionary<string, object> dbDict = new Dictionary<string, object>();
return _ExecuteNonQuery(Command, dbDict, 30, "");
}
public int ExecuteNonQuery(string Command, Dictionary<string, object> Parameters)
{
return _ExecuteNonQuery(Command, Parameters, 30, "");
}
public int ExecuteNonQuery(string Command, Dictionary<string, object> Parameters, int Timeout = 30, string ConnectionString = "")
{
return _ExecuteNonQuery(Command, Parameters, Timeout, ConnectionString);
}
private int _ExecuteNonQuery(string Command, Dictionary<string, object> Parameters, int Timeout = 30, string ConnectionString = "")
{
if (ConnectionString == "") { ConnectionString = _ConnectionString; }
switch (_ConnectorType)
{
case databaseType.MySql:
MySQLServerConnector conn = new MySQLServerConnector(ConnectionString);
return (int)conn.ExecNonQuery(Command, Parameters, Timeout);
default:
return 0;
}
}
public void ExecuteTransactionCMD(List<SQLTransactionItem> CommandList, int Timeout = 60)
{
object conn;
@@ -284,6 +357,47 @@ namespace gaseous_server.Classes
return RetTable;
}
public int ExecNonQuery(string SQL, Dictionary< string, object> Parameters, int Timeout)
{
int result = 0;
Logging.Log(Logging.LogType.Debug, "Database", "Connecting to database", null, true);
MySqlConnection conn = new MySqlConnection(DBConn);
conn.Open();
MySqlCommand cmd = new MySqlCommand
{
Connection = conn,
CommandText = SQL,
CommandTimeout = Timeout
};
foreach (string Parameter in Parameters.Keys)
{
cmd.Parameters.AddWithValue(Parameter, Parameters[Parameter]);
}
try
{
Logging.Log(Logging.LogType.Debug, "Database", "Executing sql: '" + SQL + "'", null, true);
if (Parameters.Count > 0)
{
string dictValues = string.Join(";", Parameters.Select(x => string.Join("=", x.Key, x.Value)));
Logging.Log(Logging.LogType.Debug, "Database", "Parameters: " + dictValues, null, true);
}
result = cmd.ExecuteNonQuery();
} catch (Exception ex) {
Logging.Log(Logging.LogType.Critical, "Database", "Error while executing '" + SQL + "'", ex);
Trace.WriteLine("Error executing " + SQL);
Trace.WriteLine("Full exception: " + ex.ToString());
}
Logging.Log(Logging.LogType.Debug, "Database", "Closing database connection", null, true);
conn.Close();
return result;
}
public void TransactionExecCMD(List<Dictionary<string, object>> Parameters, int Timeout)
{
var conn = new MySqlConnection(DBConn);
@@ -1,5 +1,6 @@
using System;
using System.Reflection;
using System.Text.Json.Serialization;
using IGDB;
using IGDB.Models;
@@ -151,6 +152,202 @@ namespace gaseous_server.Classes.Metadata
public AgeRatingTitle RatingTitle { get; set; }
public string[] Descriptions { get; set; }
}
public class AgeGroups
{
public AgeGroups()
{
}
public static Dictionary<string, List<AgeGroupItem>> AgeGroupings
{
get
{
return new Dictionary<string, List<AgeGroupItem>>{
{
"Adult", new List<AgeGroupItem>{ Adult_Item, Mature_Item, Teen_Item, Child_Item }
},
{
"Mature", new List<AgeGroupItem>{ Mature_Item, Teen_Item, Child_Item }
},
{
"Teen", new List<AgeGroupItem>{ Teen_Item, Child_Item }
},
{
"Child", new List<AgeGroupItem>{ Child_Item }
}
};
}
}
public static Dictionary<string, AgeGroupItem> AgeGroupingsFlat
{
get
{
return new Dictionary<string, AgeGroupItem>{
{
"Adult", Adult_Item
},
{
"Mature", Mature_Item
},
{
"Teen", Teen_Item
},
{
"Child", Child_Item
}
};
}
}
public static List<ClassificationBoardItem> ClassificationBoards
{
get
{
ClassificationBoardItem boardItem = new ClassificationBoardItem{
Board = AgeRatingCategory.ACB,
Classifications = new List<AgeRatingTitle>{
AgeRatingTitle.ACB_G, AgeRatingTitle.ACB_M, AgeRatingTitle.ACB_MA15, AgeRatingTitle.ACB_R18, AgeRatingTitle.ACB_RC
}
};
return new List<ClassificationBoardItem>{
new ClassificationBoardItem{
Board = AgeRatingCategory.ACB,
Classifications = new List<AgeRatingTitle>{
AgeRatingTitle.ACB_G,
AgeRatingTitle.ACB_M,
AgeRatingTitle.ACB_MA15,
AgeRatingTitle.ACB_R18,
AgeRatingTitle.ACB_RC
}
},
new ClassificationBoardItem{
Board = AgeRatingCategory.CERO,
Classifications = new List<AgeRatingTitle>{
AgeRatingTitle.CERO_A,
AgeRatingTitle.CERO_B,
AgeRatingTitle.CERO_C,
AgeRatingTitle.CERO_D,
AgeRatingTitle.CERO_Z
}
},
new ClassificationBoardItem{
Board = AgeRatingCategory.CLASS_IND,
Classifications = new List<AgeRatingTitle>{
AgeRatingTitle.CLASS_IND_L,
AgeRatingTitle.CLASS_IND_Ten,
AgeRatingTitle.CLASS_IND_Twelve,
AgeRatingTitle.CLASS_IND_Fourteen,
AgeRatingTitle.CLASS_IND_Sixteen,
AgeRatingTitle.CLASS_IND_Eighteen
}
}
};
}
}
readonly static AgeGroupItem Adult_Item = new AgeGroupItem{
ACB = new List<AgeRatingTitle>{ AgeRatingTitle.ACB_R18, AgeRatingTitle.ACB_RC },
CERO = new List<AgeRatingTitle>{ AgeRatingTitle.CERO_Z },
CLASS_IND = new List<AgeRatingTitle>{ AgeRatingTitle.CLASS_IND_Eighteen },
ESRB = new List<AgeRatingTitle>{ AgeRatingTitle.RP, AgeRatingTitle.AO },
GRAC = new List<AgeRatingTitle>{ AgeRatingTitle.GRAC_Eighteen },
PEGI = new List<AgeRatingTitle>{ AgeRatingTitle.Eighteen},
USK = new List<AgeRatingTitle>{ AgeRatingTitle.USK_18}
};
readonly static AgeGroupItem Mature_Item = new AgeGroupItem{
ACB = new List<AgeRatingTitle>{ AgeRatingTitle.ACB_M, AgeRatingTitle.ACB_MA15 },
CERO = new List<AgeRatingTitle>{ AgeRatingTitle.CERO_C, AgeRatingTitle.CERO_D },
CLASS_IND = new List<AgeRatingTitle>{ AgeRatingTitle.CLASS_IND_Sixteen },
ESRB = new List<AgeRatingTitle>{ AgeRatingTitle.M },
GRAC = new List<AgeRatingTitle>{ AgeRatingTitle.GRAC_Fifteen },
PEGI = new List<AgeRatingTitle>{ AgeRatingTitle.Sixteen},
USK = new List<AgeRatingTitle>{ AgeRatingTitle.USK_16}
};
readonly static AgeGroupItem Teen_Item = new AgeGroupItem{
ACB = new List<AgeRatingTitle>{ AgeRatingTitle.ACB_PG },
CERO = new List<AgeRatingTitle>{ AgeRatingTitle.CERO_B },
CLASS_IND = new List<AgeRatingTitle>{ AgeRatingTitle.CLASS_IND_Twelve, AgeRatingTitle.CLASS_IND_Fourteen },
ESRB = new List<AgeRatingTitle>{ AgeRatingTitle.T },
GRAC = new List<AgeRatingTitle>{ AgeRatingTitle.GRAC_Twelve },
PEGI = new List<AgeRatingTitle>{ AgeRatingTitle.Twelve},
USK = new List<AgeRatingTitle>{ AgeRatingTitle.USK_12}
};
readonly static AgeGroupItem Child_Item = new AgeGroupItem{
ACB = new List<AgeRatingTitle>{ AgeRatingTitle.ACB_G },
CERO = new List<AgeRatingTitle>{ AgeRatingTitle.CERO_A },
CLASS_IND = new List<AgeRatingTitle>{ AgeRatingTitle.CLASS_IND_L, AgeRatingTitle.CLASS_IND_Ten },
ESRB = new List<AgeRatingTitle>{ AgeRatingTitle.E, AgeRatingTitle.E10 },
GRAC = new List<AgeRatingTitle>{ AgeRatingTitle.GRAC_All },
PEGI = new List<AgeRatingTitle>{ AgeRatingTitle.Three, AgeRatingTitle.Seven},
USK = new List<AgeRatingTitle>{ AgeRatingTitle.USK_0, AgeRatingTitle.USK_6}
};
public class AgeGroupItem
{
public List<IGDB.Models.AgeRatingTitle> ACB { get; set; }
public List<IGDB.Models.AgeRatingTitle> CERO { get; set; }
public List<IGDB.Models.AgeRatingTitle> CLASS_IND { get; set; }
public List<IGDB.Models.AgeRatingTitle> ESRB { get; set; }
public List<IGDB.Models.AgeRatingTitle> GRAC { get; set; }
public List<IGDB.Models.AgeRatingTitle> PEGI { get; set; }
public List<IGDB.Models.AgeRatingTitle> USK { get; set; }
[JsonIgnore]
[Newtonsoft.Json.JsonIgnore]
public List<long> AgeGroupItemValues
{
get
{
List<long> values = new List<long>();
{
foreach (AgeRatingTitle ageRatingTitle in ACB)
{
values.Add((long)ageRatingTitle);
}
foreach (AgeRatingTitle ageRatingTitle in CERO)
{
values.Add((long)ageRatingTitle);
}
foreach (AgeRatingTitle ageRatingTitle in CLASS_IND)
{
values.Add((long)ageRatingTitle);
}
foreach (AgeRatingTitle ageRatingTitle in ESRB)
{
values.Add((long)ageRatingTitle);
}
foreach (AgeRatingTitle ageRatingTitle in GRAC)
{
values.Add((long)ageRatingTitle);
}
foreach (AgeRatingTitle ageRatingTitle in PEGI)
{
values.Add((long)ageRatingTitle);
}
foreach (AgeRatingTitle ageRatingTitle in USK)
{
values.Add((long)ageRatingTitle);
}
}
return values;
}
}
}
public class ClassificationBoardItem
{
public IGDB.Models.AgeRatingCategory Board { get; set; }
public List<IGDB.Models.AgeRatingTitle> Classifications { get; set; }
}
}
}
}
@@ -0,0 +1,396 @@
using System.Security.Claims;
using System.Text;
using Authentication;
using gaseous_server.Classes;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.UI.Services;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
namespace gaseous_server.Controllers
{
[ApiController]
[Route("api/v{version:apiVersion}/[controller]")]
[ApiVersion("1.0")]
[ApiVersion("1.1")]
[Authorize]
public class AccountController : Controller
{
private readonly UserManager<ApplicationUser> _userManager;
private readonly SignInManager<ApplicationUser> _signInManager;
private readonly IEmailSender _emailSender;
private readonly ILogger _logger;
public AccountController(
UserManager<ApplicationUser> userManager,
SignInManager<ApplicationUser> signInManager,
IEmailSender emailSender,
ILoggerFactory loggerFactory)
{
_userManager = userManager;
_signInManager = signInManager;
_emailSender = emailSender;
_logger = loggerFactory.CreateLogger<AccountController>();
}
[HttpPost]
[AllowAnonymous]
[Route("Login")]
public async Task<IActionResult> Login(LoginViewModel model)
{
if (ModelState.IsValid)
{
// This doesn't count login failures towards account lockout
// To enable password failures to trigger account lockout, set lockoutOnFailure: true
var result = await _signInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, lockoutOnFailure: false);
if (result.Succeeded)
{
Logging.Log(Logging.LogType.Information, "Login", model.Email + " has logged in, from IP: " + HttpContext.Connection.RemoteIpAddress?.ToString());
return Ok(result.ToString());
}
// if (result.RequiresTwoFactor)
// {
// return RedirectToAction(nameof(SendCode), new { ReturnUrl = returnUrl, RememberMe = model.RememberMe });
// }
if (result.IsLockedOut)
{
Logging.Log(Logging.LogType.Warning, "Login", model.Email + " was unable to login due to a locked account. Login attempt from IP: " + HttpContext.Connection.RemoteIpAddress?.ToString());
return Unauthorized();
}
else
{
Logging.Log(Logging.LogType.Critical, "Login", "An unknown error occurred during login by " + model.Email + ". Login attempt from IP: " + HttpContext.Connection.RemoteIpAddress?.ToString());
return Unauthorized();
}
}
// If we got this far, something failed, redisplay form
Logging.Log(Logging.LogType.Critical, "Login", "An unknown error occurred during login by " + model.Email + ". Login attempt from IP: " + HttpContext.Connection.RemoteIpAddress?.ToString());
return Unauthorized();
}
[HttpPost]
[Route("LogOff")]
public async Task<IActionResult> LogOff()
{
var userName = User.FindFirstValue(ClaimTypes.Name);
await _signInManager.SignOutAsync();
if (userName != null)
{
Logging.Log(Logging.LogType.Information, "Login", userName + " has logged out");
}
return Ok();
}
[HttpGet]
[Route("Profile/Basic")]
[Authorize]
public async Task<IActionResult> ProfileBasic()
{
ProfileBasicViewModel profile = new ProfileBasicViewModel();
profile.UserId = User.FindFirstValue(ClaimTypes.NameIdentifier);
ApplicationUser user = await _userManager.FindByIdAsync(profile.UserId);
profile.UserName = _userManager.GetUserName(HttpContext.User);
profile.EmailAddress = await _userManager.GetEmailAsync(user);
profile.Roles = new List<string>(await _userManager.GetRolesAsync(user));
profile.SecurityProfile = user.SecurityProfile;
profile.Roles.Sort();
return Ok(profile);
}
[HttpGet]
[Route("Profile/Basic/profile.js")]
[ApiExplorerSettings(IgnoreApi = true)]
[AllowAnonymous]
public async Task<IActionResult> ProfileBasicFile()
{
var user = await _userManager.GetUserAsync(User);
if (user != null)
{
ProfileBasicViewModel profile = new ProfileBasicViewModel();
profile.UserId = User.FindFirstValue(ClaimTypes.NameIdentifier);
profile.UserName = _userManager.GetUserName(HttpContext.User);
profile.EmailAddress = await _userManager.GetEmailAsync(user);
profile.Roles = new List<string>(await _userManager.GetRolesAsync(user));
profile.SecurityProfile = user.SecurityProfile;
profile.Roles.Sort();
string profileString = "var userProfile = " + Newtonsoft.Json.JsonConvert.SerializeObject(profile, Newtonsoft.Json.Formatting.Indented) + ";";
byte[] bytes = Encoding.UTF8.GetBytes(profileString);
return File(bytes, "text/javascript");
}
else
{
string profileString = "var userProfile = null;";
byte[] bytes = Encoding.UTF8.GetBytes(profileString);
return File(bytes, "text/javascript");
}
}
[HttpPost]
[Route("ChangePassword")]
[Authorize]
public async Task<IActionResult> ChangePassword(ChangePasswordViewModel model)
{
if (ModelState.IsValid)
{
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
return RedirectToAction("Login");
}
// ChangePasswordAsync changes the user password
var result = await _userManager.ChangePasswordAsync(user, model.OldPassword, model.NewPassword);
// The new password did not meet the complexity rules or
// the current password is incorrect. Add these errors to
// the ModelState and rerender ChangePassword view
if (!result.Succeeded)
{
foreach (var error in result.Errors)
{
ModelState.AddModelError(string.Empty, error.Description);
}
return Unauthorized(result);
}
// Upon successfully changing the password refresh sign-in cookie
await _signInManager.RefreshSignInAsync(user);
return Ok();
}
return NotFound();
}
[HttpGet]
[Route("Users")]
[Authorize(Roles = "Admin")]
public async Task<IActionResult> GetAllUsers()
{
List<UserViewModel> users = new List<UserViewModel>();
foreach (ApplicationUser rawUser in _userManager.Users)
{
UserViewModel user = new UserViewModel();
user.Id = rawUser.Id;
user.EmailAddress = rawUser.NormalizedEmail.ToLower();
user.LockoutEnabled = rawUser.LockoutEnabled;
user.LockoutEnd = rawUser.LockoutEnd;
user.SecurityProfile = rawUser.SecurityProfile;
// get roles
ApplicationUser? aUser = await _userManager.FindByIdAsync(rawUser.Id);
if (aUser != null)
{
IList<string> aUserRoles = await _userManager.GetRolesAsync(aUser);
user.Roles = aUserRoles.ToList();
user.Roles.Sort();
}
users.Add(user);
}
return Ok(users);
}
[HttpPost]
[Route("Users")]
[Authorize(Roles = "Admin")]
public async Task<IActionResult> NewUser(RegisterViewModel model)
{
if (ModelState.IsValid)
{
ApplicationUser user = new ApplicationUser
{
UserName = model.UserName,
NormalizedUserName = model.UserName.ToUpper(),
Email = model.Email,
NormalizedEmail = model.Email.ToUpper()
};
var result = await _userManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
// add new users to the player role
await _userManager.AddToRoleAsync(user, "Player");
Logging.Log(Logging.LogType.Information, "User Management", User.FindFirstValue(ClaimTypes.Name) + " created user " + model.Email + " with password.");
return Ok(result);
}
else
{
return Ok(result);
}
}
else
{
return NotFound();
}
}
[HttpGet]
[Route("Users/{UserId}")]
[Authorize(Roles = "Admin")]
public async Task<IActionResult> GetUser(string UserId)
{
ApplicationUser? rawUser = await _userManager.FindByIdAsync(UserId);
if (rawUser != null)
{
UserViewModel user = new UserViewModel();
user.Id = rawUser.Id;
user.EmailAddress = rawUser.NormalizedEmail.ToLower();
user.LockoutEnabled = rawUser.LockoutEnabled;
user.LockoutEnd = rawUser.LockoutEnd;
user.SecurityProfile = rawUser.SecurityProfile;
// get roles
IList<string> aUserRoles = await _userManager.GetRolesAsync(rawUser);
user.Roles = aUserRoles.ToList();
user.Roles.Sort();
return Ok(user);
}
else
{
return NotFound();
}
}
[HttpDelete]
[Route("Users/{UserId}")]
[Authorize(Roles = "Admin")]
public async Task<IActionResult> DeleteUser(string UserId)
{
// get user
ApplicationUser? user = await _userManager.FindByIdAsync(UserId);
if (user == null)
{
return NotFound();
}
else
{
await _userManager.DeleteAsync(user);
Logging.Log(Logging.LogType.Information, "User Management", User.FindFirstValue(ClaimTypes.Name) + " deleted user " + user.Email);
return Ok();
}
}
[HttpPost]
[Route("Users/{UserId}/Roles")]
[Authorize(Roles = "Admin")]
public async Task<IActionResult> SetUserRoles(string UserId, string RoleName)
{
ApplicationUser? user = await _userManager.FindByIdAsync(UserId);
if (user != null)
{
// get roles
List<string> userRoles = (await _userManager.GetRolesAsync(user)).ToList();
// delete all roles
foreach (string role in userRoles)
{
if ((new string[] { "Admin", "Gamer", "Player" }).Contains(role) )
{
await _userManager.RemoveFromRoleAsync(user, role);
}
}
// add only requested roles
switch (RoleName)
{
case "Admin":
await _userManager.AddToRoleAsync(user, "Admin");
await _userManager.AddToRoleAsync(user, "Gamer");
await _userManager.AddToRoleAsync(user, "Player");
break;
case "Gamer":
await _userManager.AddToRoleAsync(user, "Gamer");
await _userManager.AddToRoleAsync(user, "Player");
break;
case "Player":
await _userManager.AddToRoleAsync(user, "Player");
break;
default:
await _userManager.AddToRoleAsync(user, RoleName);
break;
}
return Ok();
}
else
{
return NotFound();
}
}
[HttpPost]
[Route("Users/{UserId}/SecurityProfile")]
[Authorize(Roles = "Admin")]
public async Task<IActionResult> SetUserSecurityProfile(string UserId, SecurityProfileViewModel securityProfile)
{
if (ModelState.IsValid)
{
ApplicationUser? user = await _userManager.FindByIdAsync(UserId);
if (user != null)
{
user.SecurityProfile = securityProfile;
await _userManager.UpdateAsync(user);
return Ok();
}
else
{
return NotFound();
}
}
else
{
return NotFound();
}
}
[HttpPost]
[Route("Users/{UserId}/Password")]
[Authorize(Roles = "Admin")]
public async Task<IActionResult> ResetPassword(string UserId, SetPasswordViewModel model)
{
if (ModelState.IsValid)
{
// we can reset the users password
ApplicationUser? user = await _userManager.FindByIdAsync(UserId);
if (user != null)
{
string resetToken = await _userManager.GeneratePasswordResetTokenAsync(user);
IdentityResult passwordChangeResult = await _userManager.ResetPasswordAsync(user, resetToken, model.NewPassword);
if (passwordChangeResult.Succeeded == true)
{
return Ok();
}
else
{
return Ok(passwordChangeResult);
}
}
else
{
return NotFound();
}
}
else
{
return NotFound();
}
}
}
}
@@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace gaseous_server.Controllers
@@ -9,9 +10,12 @@ namespace gaseous_server.Controllers
[ApiController]
[Route("api/v{version:apiVersion}/[controller]")]
[ApiVersion("1.0")]
[ApiVersion("1.1")]
[Authorize(Roles = "Admin,Gamer,Player")]
public class BackgroundTasksController : Controller
{
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet]
[ProducesResponseType(StatusCodes.Status200OK)]
public List<ProcessQueue.QueueItem> GetQueue()
@@ -20,10 +24,12 @@ namespace gaseous_server.Controllers
}
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet]
[Route("{TaskType}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[Authorize(Roles = "Admin")]
public ActionResult<ProcessQueue.QueueItem> ForceRun(ProcessQueue.QueueItemType TaskType, Boolean ForceRun)
{
foreach (ProcessQueue.QueueItem qi in ProcessQueue.QueueItems)
@@ -4,6 +4,7 @@ using System.IO.Compression;
using System.Linq;
using System.Threading.Tasks;
using gaseous_server.Classes;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace gaseous_server.Controllers
@@ -11,9 +12,12 @@ namespace gaseous_server.Controllers
[ApiController]
[Route("api/v{version:apiVersion}/[controller]")]
[ApiVersion("1.0")]
[ApiVersion("1.1")]
[Authorize]
public class BiosController : Controller
{
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet]
[ProducesResponseType(StatusCodes.Status200OK)]
public List<Classes.Bios.BiosItem> GetBios()
@@ -22,6 +26,7 @@ namespace gaseous_server.Controllers
}
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet]
[Route("{PlatformId}")]
[ProducesResponseType(StatusCodes.Status200OK)]
@@ -31,8 +36,10 @@ namespace gaseous_server.Controllers
}
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet]
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpHead]
[Route("zip/{PlatformId}")]
[ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)]
@@ -66,8 +73,10 @@ namespace gaseous_server.Controllers
}
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet]
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpHead]
[Route("{PlatformId}/{BiosName}")]
[ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)]
@@ -4,6 +4,7 @@ using System.IO.Compression;
using System.Linq;
using System.Threading.Tasks;
using gaseous_server.Classes;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace gaseous_server.Controllers
@@ -11,6 +12,8 @@ namespace gaseous_server.Controllers
[ApiController]
[Route("api/v{version:apiVersion}/[controller]")]
[ApiVersion("1.0")]
[ApiVersion("1.1")]
[Authorize]
public class CollectionsController : Controller
{
/// <summary>
@@ -18,6 +21,7 @@ namespace gaseous_server.Controllers
/// </summary>
/// <returns></returns>
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet]
[ProducesResponseType(StatusCodes.Status200OK)]
public List<Classes.Collections.CollectionItem> GetCollections()
@@ -32,6 +36,7 @@ namespace gaseous_server.Controllers
/// <param name="Build">Set to true to begin the collection build process</param>
/// <returns></returns>
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet]
[Route("{CollectionId}")]
[ProducesResponseType(typeof(Classes.Collections.CollectionItem), StatusCodes.Status200OK)]
@@ -59,6 +64,7 @@ namespace gaseous_server.Controllers
/// <param name="CollectionId"></param>
/// <returns></returns>
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet]
[Route("{CollectionId}/Roms")]
[ProducesResponseType(typeof(List<Classes.Collections.CollectionContents.CollectionPlatformItem>), StatusCodes.Status200OK)]
@@ -82,8 +88,10 @@ namespace gaseous_server.Controllers
/// <param name="Item"></param>
/// <returns></returns>
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpPost]
[Route("Preview")]
[Authorize(Roles = "Admin,Gamer")]
[ProducesResponseType(typeof(List<Classes.Collections.CollectionContents.CollectionPlatformItem>), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public ActionResult GetCollectionRomsPreview(Classes.Collections.CollectionItem Item)
@@ -104,6 +112,7 @@ namespace gaseous_server.Controllers
/// <param name="CollectionId"></param>
/// <returns></returns>
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet]
[Route("{CollectionId}/Roms/Zip")]
[ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)]
@@ -138,7 +147,9 @@ namespace gaseous_server.Controllers
/// <param name="Item"></param>
/// <returns></returns>
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpPost]
[Authorize(Roles = "Admin,Gamer")]
[ProducesResponseType(typeof(Classes.Collections.CollectionItem), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public ActionResult NewCollection(Classes.Collections.CollectionItem Item)
@@ -160,8 +171,10 @@ namespace gaseous_server.Controllers
/// <param name="Item"></param>
/// <returns></returns>
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpPatch]
[Route("{CollectionId}")]
[Authorize(Roles = "Admin,Gamer")]
[ProducesResponseType(typeof(Classes.Collections.CollectionItem), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public ActionResult EditCollection(long CollectionId, Classes.Collections.CollectionItem Item)
@@ -183,7 +196,9 @@ namespace gaseous_server.Controllers
/// <param name="Item"></param>
/// <returns></returns>
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpPatch]
[Authorize(Roles = "Admin,Gamer")]
[Route("{CollectionId}/AlwaysInclude")]
[ProducesResponseType(typeof(Classes.Collections.CollectionItem), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
@@ -218,7 +233,9 @@ namespace gaseous_server.Controllers
/// </summary>
/// <param name="CollectionId"></param>
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpDelete]
[Authorize(Roles = "Admin,Gamer")]
[Route("{CollectionId}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
@@ -5,6 +5,7 @@ using System.Linq;
using System.Threading.Tasks;
using gaseous_server.Classes;
using IGDB.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
@@ -12,10 +13,13 @@ namespace gaseous_server.Controllers
{
[Route("api/v{version:apiVersion}/[controller]")]
[ApiVersion("1.0")]
[ApiVersion("1.1")]
[Authorize]
[ApiController]
public class FilterController : ControllerBase
{
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet]
[ProducesResponseType(StatusCodes.Status200OK)]
//[ResponseCache(CacheProfileName = "5Minute")]
@@ -9,6 +9,7 @@ using System.Threading.Tasks;
using gaseous_server.Classes;
using gaseous_server.Classes.Metadata;
using IGDB.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.CodeAnalysis.Scripting;
@@ -18,6 +19,8 @@ namespace gaseous_server.Controllers
{
[Route("api/v{version:apiVersion}/[controller]")]
[ApiVersion("1.0")]
[ApiVersion("1.1")]
[Authorize]
[ApiController]
public class GamesController : ControllerBase
{
@@ -36,7 +39,7 @@ namespace gaseous_server.Controllers
bool sortdescending = false)
{
return Ok(GetGames(name, platform, genre, gamemode, playerperspective, theme, minrating, maxrating, sortdescending));
return Ok(GetGames(name, platform, genre, gamemode, playerperspective, theme, minrating, maxrating, "Adult", true, true, sortdescending));
}
public static List<Game> GetGames(
@@ -48,6 +51,9 @@ namespace gaseous_server.Controllers
string theme = "",
int minrating = -1,
int maxrating = -1,
string ratinggroup = "Adult",
bool includenullrating = true,
bool sortbynamethe = false,
bool sortdescending = false)
{
string whereClause = "";
@@ -170,6 +176,52 @@ namespace gaseous_server.Controllers
whereClauses.Add(tempVal);
}
if (ratinggroup.Length > 0)
{
List<long> AgeClassificationsList = new List<long>();
foreach (string ratingGroup in ratinggroup.Split(','))
{
if (AgeGroups.AgeGroupings.ContainsKey(ratingGroup))
{
List<AgeGroups.AgeGroupItem> ageGroups = AgeGroups.AgeGroupings[ratingGroup];
foreach (AgeGroups.AgeGroupItem ageGroup in ageGroups)
{
AgeClassificationsList.AddRange(ageGroup.AgeGroupItemValues);
}
}
}
if (AgeClassificationsList.Count > 0)
{
tempVal = "(view_AgeRatings.Rating IN (";
for (int i = 0; i < AgeClassificationsList.Count; i++)
{
if (i > 0)
{
tempVal += ", ";
}
string themeLabel = "@Rating" + i;
tempVal += themeLabel;
whereParams.Add(themeLabel, AgeClassificationsList[i]);
}
tempVal += ")";
tempVal += " OR ";
if (includenullrating == true)
{
tempVal += "view_AgeRatings.Rating IS NULL";
}
else
{
tempVal += "view_AgeRatings.Rating IS NOT NULL";
}
tempVal += ")";
whereClauses.Add(tempVal);
}
}
// build where clause
if (whereClauses.Count > 0)
{
@@ -199,14 +251,21 @@ namespace gaseous_server.Controllers
}
// order by clause
string orderByClause = "ORDER BY `Name` ASC";
string orderByField = "Name";
if (sortbynamethe == true)
{
orderByField = "NameThe";
}
string orderByClause = "ORDER BY `" + orderByField + "` ASC";
if (sortdescending == true)
{
orderByClause = "ORDER BY `Name` DESC";
orderByClause = "ORDER BY `" + orderByField + "` DESC";
}
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "SELECT DISTINCT Games_Roms.GameId AS ROMGameId, Game.* FROM Games_Roms LEFT JOIN Game ON Game.Id = Games_Roms.GameId LEFT JOIN Relation_Game_Genres ON Game.Id = Relation_Game_Genres.GameId LEFT JOIN Relation_Game_GameModes ON Game.Id = Relation_Game_GameModes.GameId LEFT JOIN Relation_Game_PlayerPerspectives ON Game.Id = Relation_Game_PlayerPerspectives.GameId LEFT JOIN Relation_Game_Themes ON Game.Id = Relation_Game_Themes.GameId " + whereClause + " " + havingClause + " " + orderByClause;
//string sql = "SELECT DISTINCT Games_Roms.GameId AS ROMGameId, Game.* FROM Games_Roms LEFT JOIN Game ON Game.Id = Games_Roms.GameId LEFT JOIN Relation_Game_Genres ON Game.Id = Relation_Game_Genres.GameId LEFT JOIN Relation_Game_GameModes ON Game.Id = Relation_Game_GameModes.GameId LEFT JOIN Relation_Game_PlayerPerspectives ON Game.Id = Relation_Game_PlayerPerspectives.GameId LEFT JOIN Relation_Game_Themes ON Game.Id = Relation_Game_Themes.GameId " + whereClause + " " + havingClause + " " + orderByClause;
string sql = "SELECT DISTINCT Games_Roms.GameId AS ROMGameId, Game.*, case when Game.`Name` like 'The %' then CONCAT(trim(substr(Game.`Name` from 4)), ', The') else Game.`Name` end as NameThe FROM Games_Roms LEFT JOIN Game ON Game.Id = Games_Roms.GameId LEFT JOIN Relation_Game_Genres ON Game.Id = Relation_Game_Genres.GameId LEFT JOIN Relation_Game_GameModes ON Game.Id = Relation_Game_GameModes.GameId LEFT JOIN Relation_Game_PlayerPerspectives ON Game.Id = Relation_Game_PlayerPerspectives.GameId LEFT JOIN Relation_Game_Themes ON Game.Id = Relation_Game_Themes.GameId LEFT JOIN (SELECT Relation_Game_AgeRatings.GameId, AgeRating.* FROM Relation_Game_AgeRatings JOIN AgeRating ON Relation_Game_AgeRatings.AgeRatingsId = AgeRating.Id) view_AgeRatings ON Game.Id = view_AgeRatings.GameId " + whereClause + " " + havingClause + " " + orderByClause;
List<IGDB.Models.Game> RetVal = new List<IGDB.Models.Game>();
@@ -221,6 +280,7 @@ namespace gaseous_server.Controllers
}
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet]
[Route("{GameId}")]
[ProducesResponseType(typeof(Game), StatusCodes.Status200OK)]
@@ -248,6 +308,7 @@ namespace gaseous_server.Controllers
}
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet]
[Route("{GameId}/alternativename")]
[ProducesResponseType(typeof(List<AlternativeName>), StatusCodes.Status200OK)]
@@ -280,6 +341,7 @@ namespace gaseous_server.Controllers
}
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet]
[Route("{GameId}/agerating")]
[ProducesResponseType(typeof(List<AgeRatings.GameAgeRating>), StatusCodes.Status200OK)]
@@ -312,6 +374,7 @@ namespace gaseous_server.Controllers
}
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet]
[Route("{GameId}/agerating/{RatingId}/image")]
[ProducesResponseType(typeof(FileContentResult), StatusCodes.Status200OK)]
@@ -392,6 +455,7 @@ namespace gaseous_server.Controllers
}
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet]
[Route("{GameId}/artwork")]
[ProducesResponseType(typeof(List<Artwork>), StatusCodes.Status200OK)]
@@ -422,6 +486,7 @@ namespace gaseous_server.Controllers
}
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet]
[Route("{GameId}/artwork/{ArtworkId}")]
[ProducesResponseType(typeof(Artwork), StatusCodes.Status200OK)]
@@ -457,6 +522,7 @@ namespace gaseous_server.Controllers
}
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet]
[Route("{GameId}/artwork/{ArtworkId}/image")]
[ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)]
@@ -512,6 +578,7 @@ namespace gaseous_server.Controllers
}
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet]
[Route("{GameId}/cover")]
[ProducesResponseType(typeof(Cover), StatusCodes.Status200OK)]
@@ -546,6 +613,7 @@ namespace gaseous_server.Controllers
}
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet]
[Route("{GameId}/cover/image")]
[ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)]
@@ -586,6 +654,7 @@ namespace gaseous_server.Controllers
}
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet]
[Route("{GameId}/genre")]
[ProducesResponseType(typeof(List<Genre>), StatusCodes.Status200OK)]
@@ -623,6 +692,7 @@ namespace gaseous_server.Controllers
}
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet]
[Route("{GameId}/companies")]
[ProducesResponseType(typeof(List<Dictionary<string, object>>), StatusCodes.Status200OK)]
@@ -667,6 +737,7 @@ namespace gaseous_server.Controllers
}
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet]
[Route("{GameId}/companies/{CompanyId}")]
[ProducesResponseType(typeof(Dictionary<string, object>), StatusCodes.Status200OK)]
@@ -709,6 +780,7 @@ namespace gaseous_server.Controllers
}
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet]
[Route("{GameId}/companies/{CompanyId}/image")]
[ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)]
@@ -753,6 +825,7 @@ namespace gaseous_server.Controllers
}
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet]
[Route("{GameId}/roms")]
[ProducesResponseType(typeof(Classes.Roms.GameRomObject), StatusCodes.Status200OK)]
@@ -773,6 +846,7 @@ namespace gaseous_server.Controllers
}
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet]
[Route("{GameId}/roms/{RomId}")]
[ProducesResponseType(typeof(Classes.Roms.GameRomItem), StatusCodes.Status200OK)]
@@ -801,7 +875,9 @@ namespace gaseous_server.Controllers
}
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpPatch]
[Authorize(Roles = "Admin,Gamer")]
[Route("{GameId}/roms/{RomId}")]
[ProducesResponseType(typeof(Classes.Roms.GameRomItem), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
@@ -829,7 +905,9 @@ namespace gaseous_server.Controllers
}
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpDelete]
[Authorize(Roles = "Admin,Gamer")]
[Route("{GameId}/roms/{RomId}")]
[ProducesResponseType(typeof(Classes.Roms.GameRomItem), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
@@ -857,8 +935,10 @@ namespace gaseous_server.Controllers
}
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet]
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpHead]
[Route("{GameId}/roms/{RomId}/file")]
[ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)]
@@ -894,8 +974,10 @@ namespace gaseous_server.Controllers
}
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet]
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpHead]
[Route("{GameId}/roms/{RomId}/{FileName}")]
[ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)]
@@ -931,6 +1013,7 @@ namespace gaseous_server.Controllers
}
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet]
[Route("{GameId}/romgroup/{RomGroupId}")]
[ProducesResponseType(typeof(Classes.RomMediaGroup.GameRomMediaGroupItem), StatusCodes.Status200OK)]
@@ -959,7 +1042,9 @@ namespace gaseous_server.Controllers
}
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpPost]
[Authorize(Roles = "Admin,Gamer")]
[Route("{GameId}/romgroup")]
[ProducesResponseType(typeof(Classes.RomMediaGroup.GameRomMediaGroupItem), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
@@ -986,7 +1071,9 @@ namespace gaseous_server.Controllers
}
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpPatch]
[Authorize(Roles = "Admin,Gamer")]
[Route("{GameId}/romgroup/{RomId}")]
[ProducesResponseType(typeof(Classes.RomMediaGroup.GameRomMediaGroupItem), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
@@ -1014,7 +1101,9 @@ namespace gaseous_server.Controllers
}
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpDelete]
[Authorize(Roles = "Admin,Gamer")]
[Route("{GameId}/romgroup/{RomGroupId}")]
[ProducesResponseType(typeof(Classes.RomMediaGroup.GameRomMediaGroupItem), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
@@ -1042,8 +1131,10 @@ namespace gaseous_server.Controllers
}
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet]
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpHead]
[Route("{GameId}/romgroup/{RomGroupId}/file")]
[Route("{GameId}/romgroup/{RomGroupId}/{filename}")]
@@ -1089,6 +1180,7 @@ namespace gaseous_server.Controllers
}
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet]
[Route("search")]
[ProducesResponseType(typeof(List<Game>), StatusCodes.Status200OK)]
@@ -1127,6 +1219,7 @@ namespace gaseous_server.Controllers
}
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet]
[Route("{GameId}/screenshots")]
[ProducesResponseType(typeof(List<Screenshot>), StatusCodes.Status200OK)]
@@ -1157,6 +1250,7 @@ namespace gaseous_server.Controllers
}
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet]
[Route("{GameId}/screenshots/{ScreenshotId}")]
[ProducesResponseType(typeof(Screenshot), StatusCodes.Status200OK)]
@@ -1190,6 +1284,7 @@ namespace gaseous_server.Controllers
}
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet]
[Route("{GameId}/screenshots/{ScreenshotId}/image")]
[ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)]
@@ -1233,6 +1328,7 @@ namespace gaseous_server.Controllers
}
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet]
[Route("{GameId}/videos")]
[ProducesResponseType(typeof(List<GameVideo>), StatusCodes.Status200OK)]
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.IO.Compression;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace gaseous_server.Controllers
@@ -10,9 +11,12 @@ namespace gaseous_server.Controllers
[ApiController]
[Route("api/v{version:apiVersion}/[controller]")]
[ApiVersion("1.0")]
[ApiVersion("1.1")]
[Authorize(Roles = "Admin")]
public class LibraryController : Controller
{
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet]
[ProducesResponseType(typeof(List<GameLibrary.LibraryItem>), StatusCodes.Status200OK)]
public ActionResult GetLibraries()
@@ -21,6 +25,7 @@ namespace gaseous_server.Controllers
}
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet("{LibraryId}")]
[ProducesResponseType(typeof(GameLibrary.LibraryItem), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
@@ -37,6 +42,7 @@ namespace gaseous_server.Controllers
}
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpPost]
[ProducesResponseType(typeof(GameLibrary.LibraryItem), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
@@ -58,6 +64,7 @@ namespace gaseous_server.Controllers
}
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpDelete("{LibraryId}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using gaseous_server.Classes;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace gaseous_server.Controllers
@@ -10,9 +11,12 @@ namespace gaseous_server.Controllers
[ApiController]
[Route("api/v{version:apiVersion}/[controller]")]
[ApiVersion("1.0")]
[ApiVersion("1.1")]
[Authorize(Roles = "Admin")]
public class LogsController : Controller
{
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet]
[ProducesResponseType(StatusCodes.Status200OK)]
public List<Logging.LogItem> Logs(long? StartIndex, int PageNumber = 1, int PageSize = 100)
@@ -12,15 +12,19 @@ using IGDB.Models;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.CodeAnalysis.Scripting;
using Microsoft.AspNetCore.Authorization;
namespace gaseous_server.Controllers
{
[Route("api/v{version:apiVersion}/[controller]")]
[ApiVersion("1.0")]
[ApiVersion("1.1")]
[ApiController]
[Authorize]
public class PlatformMapsController : Controller
{
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet]
[ProducesResponseType(typeof(List<PlatformMapping.PlatformMapItem>), StatusCodes.Status200OK)]
public ActionResult GetPlatformMap(bool ResetToDefault = false)
@@ -34,6 +38,7 @@ namespace gaseous_server.Controllers
}
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet]
[Route("{PlatformId}")]
[ProducesResponseType(typeof(PlatformMapping.PlatformMapItem), StatusCodes.Status200OK)]
@@ -60,10 +65,12 @@ namespace gaseous_server.Controllers
}
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpPost]
[ProducesResponseType(typeof(List<IFormFile>), StatusCodes.Status200OK)]
[RequestSizeLimit(long.MaxValue)]
[DisableRequestSizeLimit, RequestFormLimits(MultipartBodyLengthLimit = long.MaxValue, ValueLengthLimit = int.MaxValue)]
[Authorize(Roles = "Admin")]
public async Task<IActionResult> UploadPlatformMap(List<IFormFile> files)
{
Guid sessionid = Guid.NewGuid();
@@ -115,6 +122,7 @@ namespace gaseous_server.Controllers
}
// [MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpPost]
// [Route("{PlatformId}")]
// [ProducesResponseType(typeof(PlatformMapping.PlatformMapItem), StatusCodes.Status200OK)]
@@ -143,10 +151,12 @@ namespace gaseous_server.Controllers
// }
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpPatch]
[Route("{PlatformId}")]
[ProducesResponseType(typeof(PlatformMapping.PlatformMapItem), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[Authorize(Roles = "Admin")]
public ActionResult EditPlatformMap(long PlatformId, PlatformMapping.PlatformMapItem Map)
{
try
@@ -9,6 +9,7 @@ using gaseous_server.Classes;
using gaseous_server.Classes.Metadata;
using gaseous_server.Models;
using IGDB.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.CodeAnalysis.Scripting;
@@ -17,10 +18,13 @@ namespace gaseous_server.Controllers
{
[Route("api/v{version:apiVersion}/[controller]")]
[ApiVersion("1.0")]
[ApiVersion("1.1")]
[Authorize]
[ApiController]
public class PlatformsController : Controller
{
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet]
[ProducesResponseType(typeof(List<Platform>), StatusCodes.Status200OK)]
public ActionResult Platform()
@@ -46,6 +50,7 @@ namespace gaseous_server.Controllers
}
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet]
[Route("{PlatformId}")]
[ProducesResponseType(typeof(Platform), StatusCodes.Status200OK)]
@@ -72,6 +77,7 @@ namespace gaseous_server.Controllers
}
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet]
[Route("{PlatformId}/platformlogo")]
[ProducesResponseType(typeof(PlatformLogo), StatusCodes.Status200OK)]
@@ -105,6 +111,7 @@ namespace gaseous_server.Controllers
}
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet]
[Route("{PlatformId}/platformlogo/image")]
[ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)]
@@ -9,6 +9,7 @@ using System.Threading.Tasks;
using gaseous_server.Classes;
using gaseous_server.Classes.Metadata;
using IGDB.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.CodeAnalysis.Scripting;
@@ -18,11 +19,15 @@ namespace gaseous_server.Controllers
{
[Route("api/v{version:apiVersion}/[controller]")]
[ApiVersion("1.0")]
[ApiVersion("1.1")]
[Authorize]
[ApiController]
public class RomsController : ControllerBase
{
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpPost]
[Authorize(Roles = "Admin,Gamer")]
[ProducesResponseType(typeof(List<IFormFile>), StatusCodes.Status200OK)]
[RequestSizeLimit(long.MaxValue)]
[DisableRequestSizeLimit, RequestFormLimits(MultipartBodyLengthLimit = long.MaxValue, ValueLengthLimit = int.MaxValue)]
@@ -5,6 +5,7 @@ using System.Threading.Tasks;
using gaseous_server.Classes;
using IGDB;
using IGDB.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using static gaseous_server.Classes.Metadata.Games;
@@ -14,6 +15,8 @@ namespace gaseous_server.Controllers
[ApiController]
[Route("api/v{version:apiVersion}/[controller]")]
[ApiVersion("1.0")]
[ApiVersion("1.1")]
[Authorize]
public class SearchController : Controller
{
private static IGDBClient igdb = new IGDBClient(
@@ -23,6 +26,7 @@ namespace gaseous_server.Controllers
);
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet]
[Route("Platform")]
[ProducesResponseType(typeof(List<Platform>), StatusCodes.Status200OK)]
@@ -45,6 +49,7 @@ namespace gaseous_server.Controllers
}
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet]
[Route("Game")]
[ProducesResponseType(typeof(List<Game>), StatusCodes.Status200OK)]
@@ -6,6 +6,7 @@ using System.Security.Cryptography;
using System.Threading.Tasks;
using gaseous_server.Classes;
using gaseous_signature_parser.models.RomSignatureObject;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
// For more information on enabling MVC for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860
@@ -15,6 +16,8 @@ namespace gaseous_server.Controllers
[ApiController]
[Route("api/v{version:apiVersion}/[controller]/[action]")]
[ApiVersion("1.0")]
[ApiVersion("1.1")]
[Authorize]
public class SignaturesController : ControllerBase
{
/// <summary>
@@ -22,6 +25,7 @@ namespace gaseous_server.Controllers
/// </summary>
/// <returns>Number of sources, publishers, games, and rom signatures in the database</returns>
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet]
[ProducesResponseType(StatusCodes.Status200OK)]
public Models.Signatures_Status Status()
@@ -30,6 +34,7 @@ namespace gaseous_server.Controllers
}
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet]
[ProducesResponseType(StatusCodes.Status200OK)]
public List<Models.Signatures_Games> GetSignature(string md5 = "", string sha1 = "")
@@ -44,6 +49,7 @@ namespace gaseous_server.Controllers
}
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet]
[ProducesResponseType(StatusCodes.Status200OK)]
public List<Models.Signatures_Games> GetByTosecName(string TosecName = "")
@@ -4,8 +4,11 @@ using System.Data;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using gaseous_server.Classes;
using gaseous_server.Classes.Metadata;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace gaseous_server.Controllers
@@ -13,9 +16,12 @@ namespace gaseous_server.Controllers
[ApiController]
[Route("api/v{version:apiVersion}/[controller]")]
[ApiVersion("1.0")]
[ApiVersion("1.1")]
[Authorize]
public class SystemController : Controller
{
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet]
[ProducesResponseType(StatusCodes.Status200OK)]
public SystemInfo GetSystemStatus()
@@ -56,6 +62,7 @@ namespace gaseous_server.Controllers
}
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet]
[Route("Version")]
[ProducesResponseType(StatusCodes.Status200OK)]
@@ -64,13 +71,30 @@ namespace gaseous_server.Controllers
}
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet]
[Route("VersionFile")]
[AllowAnonymous]
[ProducesResponseType(StatusCodes.Status200OK)]
public FileContentResult GetSystemVersionAsFile() {
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
// get age ratings dictionary
Dictionary<int, string> AgeRatingsStrings = new Dictionary<int, string>();
foreach(IGDB.Models.AgeRatingTitle ageRatingTitle in Enum.GetValues(typeof(IGDB.Models.AgeRatingTitle)) )
{
AgeRatingsStrings.Add((int)ageRatingTitle, ageRatingTitle.ToString());
}
string ver = "var AppVersion = \"" + Assembly.GetExecutingAssembly().GetName().Version.ToString() + "\";" + Environment.NewLine +
"var DBSchemaVersion = \"" + db.GetDatabaseSchemaVersion() + "\";";
"var DBSchemaVersion = \"" + db.GetDatabaseSchemaVersion() + "\";" + Environment.NewLine +
"var FirstRunStatus = " + Config.ReadSetting("FirstRunStatus", "0") + ";" + Environment.NewLine +
"var AgeRatingStrings = " + JsonSerializer.Serialize(AgeRatingsStrings, new JsonSerializerOptions{
WriteIndented = true
}) + ";" + Environment.NewLine +
"var AgeRatingGroups = " + JsonSerializer.Serialize(AgeRatings.AgeGroups.AgeGroupingsFlat, new JsonSerializerOptions{
WriteIndented = true
}) + ";";
byte[] bytes = Encoding.UTF8.GetBytes(ver);
return File(bytes, "text/javascript");
}
@@ -0,0 +1,78 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using Authentication;
using gaseous_server.Classes;
using gaseous_server.Classes.Metadata;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
namespace gaseous_server.Controllers
{
[ApiController]
[Route("api/v{version:apiVersion}/[controller]")]
[ApiVersion("1.1")]
[Authorize]
public class FirstSetupController : Controller
{
private readonly UserManager<ApplicationUser> _userManager;
private readonly SignInManager<ApplicationUser> _signInManager;
public FirstSetupController(
UserManager<ApplicationUser> userManager,
SignInManager<ApplicationUser> signInManager)
{
_userManager = userManager;
_signInManager = signInManager;
}
[MapToApiVersion("1.1")]
[HttpPost]
[Route("0")]
[AllowAnonymous]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<ActionResult> CreateAdminAccount(Authentication.RegisterViewModel model)
{
if (Config.ReadSetting("FirstRunStatus", "0") == "0")
{
if (ModelState.IsValid)
{
ApplicationUser user = new ApplicationUser
{
UserName = model.UserName,
NormalizedUserName = model.UserName.ToUpper(),
Email = model.Email,
NormalizedEmail = model.Email.ToUpper(),
SecurityProfile = new SecurityProfileViewModel()
};
var result = await _userManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
await _userManager.AddToRoleAsync(user, "Player");
await _userManager.AddToRoleAsync(user, "Gamer");
await _userManager.AddToRoleAsync(user, "Admin");
await _signInManager.SignInAsync(user, isPersistent: true);
Config.SetSetting("FirstRunStatus", "1");
return Ok();
}
}
return NotFound();
}
else
{
return NotFound();
}
}
}
}
@@ -0,0 +1,413 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using Authentication;
using gaseous_server.Classes;
using gaseous_server.Classes.Metadata;
using IGDB.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Scripting;
using static gaseous_server.Classes.Metadata.AgeRatings;
namespace gaseous_server.Controllers.v1_1
{
[Route("api/v{version:apiVersion}/[controller]")]
[ApiVersion("1.1")]
[ApiController]
[Authorize]
public class GamesController: ControllerBase
{
private readonly UserManager<ApplicationUser> _userManager;
private readonly SignInManager<ApplicationUser> _signInManager;
public GamesController(
UserManager<ApplicationUser> userManager,
SignInManager<ApplicationUser> signInManager)
{
_userManager = userManager;
_signInManager = signInManager;
}
[MapToApiVersion("1.1")]
[HttpPost]
[ProducesResponseType(typeof(List<Game>), StatusCodes.Status200OK)]
public async Task<IActionResult> Game_v1_1(GameSearchModel model)
{
var user = await _userManager.GetUserAsync(User);
if (user != null)
{
// apply security profile filtering
List<string> RemoveAgeGroups = new List<string>();
switch (user.SecurityProfile.AgeRestrictionPolicy.MaximumAgeRestriction.ToLower())
{
case "adult":
break;
case "mature":
RemoveAgeGroups.Add("Adult");
break;
case "teen":
RemoveAgeGroups.Add("Adult");
RemoveAgeGroups.Add("Mature");
break;
case "child":
RemoveAgeGroups.Add("Adult");
RemoveAgeGroups.Add("Mature");
RemoveAgeGroups.Add("Teen");
break;
}
foreach (string RemoveAgeGroup in RemoveAgeGroups)
{
if (model.GameAgeRating.AgeGroupings.Contains(RemoveAgeGroup))
{
model.GameAgeRating.AgeGroupings.Remove(RemoveAgeGroup);
}
}
if (user.SecurityProfile.AgeRestrictionPolicy.IncludeUnrated == false)
{
model.GameAgeRating.IncludeUnrated = false;
}
return Ok(GetGames(model));
}
else
{
return Unauthorized();
}
}
public class GameSearchModel
{
public string Name { get; set; }
public List<string> Platform { get; set; }
public List<string> Genre { get; set; }
public List<string> GameMode { get; set; }
public List<string> PlayerPerspective { get; set; }
public List<string> Theme { get; set; }
public GameRatingItem GameRating { get; set; } = new GameRatingItem();
public GameAgeRatingItem GameAgeRating { get; set; } = new GameAgeRatingItem();
public GameSortingItem Sorting { get; set; } = new GameSortingItem();
public class GameRatingItem
{
public int MinimumRating { get; set; } = -1;
public int MinimumRatingCount { get; set; } = -1;
public int MaximumRating { get; set; } = -1;
public int MaximumRatingCount { get; set; } = -1;
public bool IncludeUnrated { get; set; } = false;
}
public class GameAgeRatingItem
{
public List<string> AgeGroupings { get; set; } = new List<string>{ "Child", "Teen", "Mature", "Adult" };
public bool IncludeUnrated { get; set; } = true;
}
public class GameSortingItem
{
public SortField SortBy { get; set; } = SortField.NameThe;
public bool SortAscending { get; set; } = true;
public enum SortField
{
Name,
NameThe,
Rating,
RatingCount
}
}
}
public static List<Game> GetGames(GameSearchModel model)
{
string whereClause = "";
string havingClause = "";
Dictionary<string, object> whereParams = new Dictionary<string, object>();
List<string> whereClauses = new List<string>();
List<string> havingClauses = new List<string>();
string tempVal = "";
if (model.Name.Length > 0)
{
tempVal = "`Name` LIKE @Name";
whereParams.Add("@Name", "%" + model.Name + "%");
havingClauses.Add(tempVal);
}
if (model.GameRating != null)
{
List<string> ratingClauses = new List<string>();
if (model.GameRating.MinimumRating != -1)
{
string ratingTempMinVal = "totalRating >= @totalMinRating";
whereParams.Add("@totalMinRating", model.GameRating.MinimumRating);
ratingClauses.Add(ratingTempMinVal);
}
if (model.GameRating.MaximumRating != -1)
{
string ratingTempMaxVal = "totalRating <= @totalMaxRating";
whereParams.Add("@totalMaxRating", model.GameRating.MaximumRating);
ratingClauses.Add(ratingTempMaxVal);
}
if (model.GameRating.MinimumRatingCount != -1)
{
string ratingTempMinCountVal = "totalRatingCount >= @totalMinRatingCount";
whereParams.Add("@totalMinRatingCount", model.GameRating.MinimumRatingCount);
ratingClauses.Add(ratingTempMinCountVal);
}
if (model.GameRating.MaximumRatingCount != -1)
{
string ratingTempMaxCountVal = "totalRatingCount <= @totalMaxRatingCount";
whereParams.Add("@totalMaxRatingCount", model.GameRating.MaximumRatingCount);
ratingClauses.Add(ratingTempMaxCountVal);
}
// generate rating sub clause
string ratingClauseValue = "";
if (ratingClauses.Count > 0)
{
foreach (string ratingClause in ratingClauses)
{
if (ratingClauseValue.Length > 0)
{
ratingClauseValue += " AND ";
}
ratingClauseValue += ratingClause;
}
}
string unratedClause = "totalRating IS NOT NULL";
if (model.GameRating.IncludeUnrated == true)
{
unratedClause = "totalRating IS NULL";
}
if (ratingClauseValue.Length > 0)
{
havingClauses.Add("((" + ratingClauseValue + ") OR " + unratedClause + ")");
}
}
if (model.Platform.Count > 0)
{
tempVal = "Games_Roms.PlatformId IN (";
for (int i = 0; i < model.Platform.Count; i++)
{
if (i > 0)
{
tempVal += ", ";
}
string platformLabel = "@Platform" + i;
tempVal += platformLabel;
whereParams.Add(platformLabel, model.Platform[i]);
}
tempVal += ")";
whereClauses.Add(tempVal);
}
if (model.Genre.Count > 0)
{
tempVal = "Relation_Game_Genres.GenresId IN (";
for (int i = 0; i < model.Genre.Count; i++)
{
if (i > 0)
{
tempVal += " AND ";
}
string genreLabel = "@Genre" + i;
tempVal += genreLabel;
whereParams.Add(genreLabel, model.Genre[i]);
}
tempVal += ")";
whereClauses.Add(tempVal);
}
if (model.GameMode.Count > 0)
{
tempVal = "Relation_Game_GameModes.GameModesId IN (";
for (int i = 0; i < model.GameMode.Count; i++)
{
if (i > 0)
{
tempVal += " AND ";
}
string gameModeLabel = "@GameMode" + i;
tempVal += gameModeLabel;
whereParams.Add(gameModeLabel, model.GameMode[i]);
}
tempVal += ")";
whereClauses.Add(tempVal);
}
if (model.PlayerPerspective.Count > 0)
{
tempVal = "Relation_Game_PlayerPerspectives.PlayerPerspectivesId IN (";
for (int i = 0; i < model.PlayerPerspective.Count; i++)
{
if (i > 0)
{
tempVal += " AND ";
}
string playerPerspectiveLabel = "@PlayerPerspective" + i;
tempVal += playerPerspectiveLabel;
whereParams.Add(playerPerspectiveLabel, model.PlayerPerspective[i]);
}
tempVal += ")";
whereClauses.Add(tempVal);
}
if (model.Theme.Count > 0)
{
tempVal = "Relation_Game_Themes.ThemesId IN (";
for (int i = 0; i < model.Theme.Count; i++)
{
if (i > 0)
{
tempVal += " AND ";
}
string themeLabel = "@Theme" + i;
tempVal += themeLabel;
whereParams.Add(themeLabel, model.Theme[i]);
}
tempVal += ")";
whereClauses.Add(tempVal);
}
if (model.GameAgeRating != null)
{
if (model.GameAgeRating.AgeGroupings.Count > 0)
{
List<long> AgeClassificationsList = new List<long>();
foreach (string ratingGroup in model.GameAgeRating.AgeGroupings)
{
if (AgeGroups.AgeGroupings.ContainsKey(ratingGroup))
{
List<AgeGroups.AgeGroupItem> ageGroups = AgeGroups.AgeGroupings[ratingGroup];
foreach (AgeGroups.AgeGroupItem ageGroup in ageGroups)
{
AgeClassificationsList.AddRange(ageGroup.AgeGroupItemValues);
}
}
}
if (AgeClassificationsList.Count > 0)
{
AgeClassificationsList = new HashSet<long>(AgeClassificationsList).ToList();
tempVal = "(view_AgeRatings.Rating IN (";
for (int i = 0; i < AgeClassificationsList.Count; i++)
{
if (i > 0)
{
tempVal += ", ";
}
string themeLabel = "@Rating" + i;
tempVal += themeLabel;
whereParams.Add(themeLabel, AgeClassificationsList[i]);
}
tempVal += ")";
if (model.GameAgeRating.IncludeUnrated == true)
{
tempVal += " OR view_AgeRatings.Rating IS NULL";
}
tempVal += ")";
whereClauses.Add(tempVal);
}
}
}
// build where clause
if (whereClauses.Count > 0)
{
whereClause = "WHERE ";
for (int i = 0; i < whereClauses.Count; i++)
{
if (i > 0)
{
whereClause += " AND ";
}
whereClause += whereClauses[i];
}
}
// build having clause
if (havingClauses.Count > 0)
{
havingClause = "HAVING ";
for (int i = 0; i < havingClauses.Count; i++)
{
if (i > 0)
{
havingClause += " AND ";
}
havingClause += havingClauses[i];
}
}
// order by clause
string orderByField = "NameThe";
string orderByOrder = "ASC";
if (model.Sorting != null)
{
switch(model.Sorting.SortBy)
{
case GameSearchModel.GameSortingItem.SortField.NameThe:
orderByField = "NameThe";
break;
case GameSearchModel.GameSortingItem.SortField.Name:
orderByField = "Name";
break;
case GameSearchModel.GameSortingItem.SortField.Rating:
orderByField = "TotalRating";
break;
case GameSearchModel.GameSortingItem.SortField.RatingCount:
orderByField = "TotalRatingCount";
break;
default:
orderByField = "NameThe";
break;
}
if (model.Sorting.SortAscending == true)
{
orderByOrder = "ASC";
}
else
{
orderByOrder = "DESC";
}
}
string orderByClause = "ORDER BY `" + orderByField + "` " + orderByOrder;
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "SELECT DISTINCT Games_Roms.GameId AS ROMGameId, Game.*, case when Game.`Name` like 'The %' then CONCAT(trim(substr(Game.`Name` from 4)), ', The') else Game.`Name` end as NameThe FROM Games_Roms LEFT JOIN Game ON Game.Id = Games_Roms.GameId LEFT JOIN Relation_Game_Genres ON Game.Id = Relation_Game_Genres.GameId LEFT JOIN Relation_Game_GameModes ON Game.Id = Relation_Game_GameModes.GameId LEFT JOIN Relation_Game_PlayerPerspectives ON Game.Id = Relation_Game_PlayerPerspectives.GameId LEFT JOIN Relation_Game_Themes ON Game.Id = Relation_Game_Themes.GameId LEFT JOIN (SELECT Relation_Game_AgeRatings.GameId, AgeRating.* FROM Relation_Game_AgeRatings JOIN AgeRating ON Relation_Game_AgeRatings.AgeRatingsId = AgeRating.Id) view_AgeRatings ON Game.Id = view_AgeRatings.GameId " + whereClause + " " + havingClause + " " + orderByClause;
List<IGDB.Models.Game> RetVal = new List<IGDB.Models.Game>();
DataTable dbResponse = db.ExecuteCMD(sql, whereParams);
foreach (DataRow dr in dbResponse.Rows)
{
//RetVal.Add(Classes.Metadata.Games.GetGame((long)dr["ROMGameId"], false, false));
RetVal.Add(Classes.Metadata.Games.GetGame(dr));
}
return RetVal;
}
}
}
@@ -0,0 +1,65 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using gaseous_server.Classes;
using gaseous_server.Classes.Metadata;
using IGDB.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.CodeAnalysis.Scripting;
using static gaseous_server.Classes.Metadata.AgeRatings;
namespace gaseous_server.Controllers.v1_1
{
[Route("api/v{version:apiVersion}/[controller]")]
[ApiVersion("1.1")]
[ApiController]
public class RatingsController: ControllerBase
{
[MapToApiVersion("1.1")]
[HttpGet]
[Authorize]
[Route("Images/{RatingBoard}/{RatingId}/image.svg")]
[ProducesResponseType(typeof(FileContentResult), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public ActionResult RatingsImageById(string RatingBoard, int RatingId)
{
IGDB.Models.AgeRatingTitle RatingTitle = (AgeRatingTitle)RatingId;
string resourceName = "gaseous_server.Assets.Ratings." + RatingBoard + "." + RatingTitle.ToString() + ".svg";
var assembly = Assembly.GetExecutingAssembly();
string[] resources = assembly.GetManifestResourceNames();
if (resources.Contains(resourceName))
{
using (Stream stream = assembly.GetManifestResourceStream(resourceName))
using (StreamReader reader = new StreamReader(stream))
{
byte[] filedata = new byte[stream.Length];
stream.Read(filedata, 0, filedata.Length);
string filename = RatingBoard + "-" + RatingTitle.ToString() + ".svg";
string contentType = "image/svg+xml";
var cd = new System.Net.Mime.ContentDisposition
{
FileName = filename,
Inline = true,
};
Response.Headers.Add("Content-Disposition", cd.ToString());
Response.Headers.Add("Cache-Control", "public, max-age=604800");
return File(filedata, contentType);
}
}
return NotFound();
}
}
}
+192 -5
View File
@@ -7,8 +7,12 @@ using gaseous_server.SignatureIngestors.XML;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Versioning;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.OpenApi.Models;
using Authentication;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.UI.Services;
Logging.WriteToDiskOnly = true;
Logging.Log(Logging.LogType.Information, "Startup", "Starting Gaseous Server " + Assembly.GetExecutingAssembly().GetName().Version);
@@ -117,11 +121,14 @@ builder.Services.AddApiVersioning(config =>
config.DefaultApiVersion = new ApiVersion(1, 0);
config.AssumeDefaultVersionWhenUnspecified = true;
config.ReportApiVersions = true;
config.ApiVersionReader = ApiVersionReader.Combine(new UrlSegmentApiVersionReader(),
new HeaderApiVersionReader("x-api-version"),
new MediaTypeApiVersionReader("x-api-version"));
});
builder.Services.AddApiVersioning(setup =>
{
setup.ApiVersionReader = new UrlSegmentApiVersionReader();
});
// builder.Services.AddApiVersioning(setup =>
// {
// setup.ApiVersionReader = new UrlSegmentApiVersionReader();
// });
builder.Services.AddVersionedApiExplorer(setup =>
{
setup.GroupNameFormat = "'v'VVV";
@@ -166,6 +173,24 @@ builder.Services.AddSwaggerGen(options =>
}
});
options.SwaggerDoc("v1.1", new OpenApiInfo
{
Version = "v1.1",
Title = "Gaseous Server API",
Description = "An API for managing the Gaseous Server",
TermsOfService = new Uri("https://github.com/gaseous-project/gaseous-server"),
Contact = new OpenApiContact
{
Name = "GitHub Repository",
Url = new Uri("https://github.com/gaseous-project/gaseous-server")
},
License = new OpenApiLicense
{
Name = "Gaseous Server License",
Url = new Uri("https://github.com/gaseous-project/gaseous-server/blob/main/LICENSE")
}
});
// using System.Reflection;
var xmlFilename = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
options.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, xmlFilename));
@@ -173,19 +198,115 @@ builder.Services.AddSwaggerGen(options =>
);
builder.Services.AddHostedService<TimedHostedService>();
// identity
builder.Services.AddIdentity<ApplicationUser, ApplicationRole>(options =>
{
options.Password.RequireDigit = true;
options.Password.RequireLowercase = true;
options.Password.RequireNonAlphanumeric = false;
options.Password.RequireUppercase = true;
options.Password.RequiredLength = 10;
options.User.AllowedUserNameCharacters = null;
options.User.RequireUniqueEmail = true;
options.SignIn.RequireConfirmedPhoneNumber = false;
options.SignIn.RequireConfirmedEmail = false;
options.SignIn.RequireConfirmedAccount = false;
})
.AddUserStore<UserStore>()
.AddRoleStore<RoleStore>()
.AddDefaultTokenProviders()
.AddDefaultUI()
;
builder.Services.ConfigureApplicationCookie(options =>
{
options.Cookie.Name = "Gaseous.Identity";
options.ExpireTimeSpan = TimeSpan.FromDays(90);
options.SlidingExpiration = true;
});
// builder.Services.AddIdentityCore<ApplicationUser>(options => {
// options.SignIn.RequireConfirmedAccount = false;
// options.User.RequireUniqueEmail = true;
// options.Password.RequireDigit = false;
// options.Password.RequiredLength = 10;
// options.Password.RequireNonAlphanumeric = false;
// options.Password.RequireUppercase = false;
// options.Password.RequireLowercase = false;
// });
builder.Services.AddScoped<UserStore>();
builder.Services.AddScoped<RoleStore>();
builder.Services.AddTransient<IUserStore<ApplicationUser>, UserStore>();
builder.Services.AddTransient<IRoleStore<ApplicationRole>, RoleStore>();
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("Admin", policy => policy.RequireRole("Admin"));
options.AddPolicy("Gamer", policy => policy.RequireRole("Gamer"));
options.AddPolicy("Player", policy => policy.RequireRole("Player"));
});
// builder.Services.AddControllersWithViews(options =>
// {
// options.Filters.Add(new Microsoft.AspNetCore.Mvc.ValidateAntiForgeryTokenAttribute());
// });
var app = builder.Build();
// Configure the HTTP request pipeline.
//if (app.Environment.IsDevelopment())
//{
app.UseSwagger();
app.UseSwaggerUI();
app.UseSwaggerUI(options =>
{
options.SwaggerEndpoint($"/swagger/v1/swagger.json", "v1.0");
options.SwaggerEndpoint($"/swagger/v1.1/swagger.json", "v1.1");
}
);
//}
//app.UseHttpsRedirection();
app.UseResponseCaching();
// set up system roles
using (var scope = app.Services.CreateScope())
{
var roleManager = scope.ServiceProvider.GetRequiredService<RoleStore>();
var roles = new[] { "Admin", "Gamer", "Player" };
foreach (var role in roles)
{
if (await roleManager.FindByNameAsync(role, CancellationToken.None) == null)
{
ApplicationRole applicationRole = new ApplicationRole();
applicationRole.Name = role;
applicationRole.NormalizedName = role.ToUpper();
await roleManager.CreateAsync(applicationRole, CancellationToken.None);
}
}
// set up administrator account
var userManager = scope.ServiceProvider.GetRequiredService<UserStore>();
if (await userManager.FindByNameAsync("admin@localhost", CancellationToken.None) == null)
{
ApplicationUser adminUser = new ApplicationUser{
Id = Guid.NewGuid().ToString(),
Email = "admin@localhost",
NormalizedEmail = "ADMIN@LOCALHOST",
EmailConfirmed = true,
UserName = "administrator",
NormalizedUserName = "ADMINISTRATOR"
};
//set user password
PasswordHasher<ApplicationUser> ph = new PasswordHasher<ApplicationUser>();
adminUser.PasswordHash = ph.HashPassword(adminUser, "letmein");
await userManager.CreateAsync(adminUser, CancellationToken.None);
await userManager.AddToRoleAsync(adminUser, "Admin", CancellationToken.None);
}
}
app.UseAuthorization();
app.UseDefaultFiles();
@@ -197,6 +318,72 @@ app.UseStaticFiles(new StaticFileOptions
app.MapControllers();
// emergency password recovery if environment variable is set
// process:
// - set the environment variable "recoveraccount" to the email address of the account to be recovered
// - when the server starts the password will be reset to a random string and saved in the library
// directory with the name RecoverAccount.txt
// - user should copy this password and remove the "recoveraccount" environment variable and the
// RecoverAccount.txt file
// - the server will not start while the RecoverAccount.txt file exists
string PasswordRecoveryFile = Path.Combine(Config.LibraryConfiguration.LibraryRootDirectory, "RecoverAccount.txt");
if (!String.IsNullOrEmpty(Environment.GetEnvironmentVariable("recoveraccount")))
{
if (File.Exists(PasswordRecoveryFile))
{
// password has already been set - do nothing and just exit
Logging.Log(Logging.LogType.Critical, "Server Startup", "Unable to start while recoveraccount environment varibale is set and RecoverAccount.txt file exists.", null, true);
Environment.Exit(0);
}
else
{
// generate and save the password to disk
int length = 10;
string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()_+";
var random = new Random();
string password = new string(Enumerable.Repeat(chars, length).Select(s => s[random.Next(s.Length)]).ToArray());
File.WriteAllText(PasswordRecoveryFile, password);
// reset the password
using (var scope = app.Services.CreateScope())
{
var userManager = scope.ServiceProvider.GetRequiredService<UserStore>();
if (await userManager.FindByNameAsync(Environment.GetEnvironmentVariable("recoveraccount"), CancellationToken.None) != null)
{
ApplicationUser User = await userManager.FindByEmailAsync(Environment.GetEnvironmentVariable("recoveraccount"), CancellationToken.None);
//set user password
PasswordHasher<ApplicationUser> ph = new PasswordHasher<ApplicationUser>();
User.PasswordHash = ph.HashPassword(User, password);
await userManager.SetPasswordHashAsync(User, User.PasswordHash, CancellationToken.None);
Logging.Log(Logging.LogType.Information, "Server Startup", "Password reset complete, remove the recoveraccount environment variable and RecoverAccount.text file to allow server start.", null, true);
Environment.Exit(0);
}
else
{
Logging.Log(Logging.LogType.Critical, "Server Startup", "Account to recover not found.", null, true);
Environment.Exit(0);
}
}
}
}
else
{
// check if RecoverAccount.text file is present
if (File.Exists(PasswordRecoveryFile))
{
// cannot start while password recovery file exists
Logging.Log(Logging.LogType.Critical, "Server Startup", "Unable to start while RecoverAccount.txt file exists. Remove the file and try again.", null, true);
Environment.Exit(0);
}
}
// setup library directories
Config.LibraryConfiguration.InitLibrary();
@@ -0,0 +1,585 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Security.Claims;
using Authentication;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.UI.Services;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
namespace gaseous_server.Controllers
{
[ApiController]
[Route("Account")]
[Authorize]
public class OldAccountController : Controller
{
private readonly UserManager<ApplicationUser> _userManager;
private readonly SignInManager<ApplicationUser> _signInManager;
private readonly IEmailSender _emailSender;
private readonly ILogger _logger;
public OldAccountController(
UserManager<ApplicationUser> userManager,
SignInManager<ApplicationUser> signInManager,
IEmailSender emailSender,
ILoggerFactory loggerFactory)
{
_userManager = userManager;
_signInManager = signInManager;
_emailSender = emailSender;
_logger = loggerFactory.CreateLogger<OldAccountController>();
}
//
// GET: /Account/Login
[HttpGet]
[AllowAnonymous]
[Route("Login")]
public IActionResult Login(string returnUrl = null)
{
ViewData["ReturnUrl"] = returnUrl;
return View();
}
//
// POST: /Account/Login
[HttpPost]
[AllowAnonymous]
[Route("Login")]
public async Task<IActionResult> Login(LoginViewModel model, string returnUrl = null)
{
ViewData["ReturnUrl"] = returnUrl;
if (ModelState.IsValid)
{
// This doesn't count login failures towards account lockout
// To enable password failures to trigger account lockout, set lockoutOnFailure: true
var result = await _signInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, lockoutOnFailure: false);
if (result.Succeeded)
{
_logger.LogInformation(1, "User logged in.");
return RedirectToLocal(returnUrl);
}
if (result.RequiresTwoFactor)
{
return RedirectToAction(nameof(SendCode), new { ReturnUrl = returnUrl, RememberMe = model.RememberMe });
}
if (result.IsLockedOut)
{
_logger.LogWarning(2, "User account locked out.");
return View("Lockout");
}
else
{
ModelState.AddModelError(string.Empty, "Invalid login attempt.");
return View(model);
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
//
// GET: /Account/Register
[HttpGet]
[AllowAnonymous]
[Route("Register")]
public IActionResult Register(string returnUrl = null)
{
ViewData["ReturnUrl"] = returnUrl;
return View();
}
//
// POST: /Account/Register
[HttpPost]
[AllowAnonymous]
[Route("Register")]
public async Task<IActionResult> Register(RegisterViewModel model, string returnUrl = null)
{
// ViewData["ReturnUrl"] = returnUrl;
if (ModelState.IsValid)
{
ApplicationUser user = new ApplicationUser
{
UserName = model.UserName,
NormalizedUserName = model.UserName.ToUpper(),
Email = model.Email,
NormalizedEmail = model.Email.ToUpper()
};
var result = await _userManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
// For more information on how to enable account confirmation and password reset please visit http://go.microsoft.com/fwlink/?LinkID=532713
// Send an email with this link
//var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
//var callbackUrl = Url.Action("ConfirmEmail", "Account", new { userId = user.Id, code = code }, protocol: HttpContext.Request.Scheme);
//await _emailSender.SendEmailAsync(model.Email, "Confirm your account",
// "Please confirm your account by clicking this link: <a href=\"" + callbackUrl + "\">link</a>");
// add all users to the member role
await _userManager.AddToRoleAsync(user, "Player");
await _signInManager.SignInAsync(user, isPersistent: false);
_logger.LogInformation(3, "User created a new account with password.");
return RedirectToLocal(returnUrl);
}
AddErrors(result);
}
// If we got this far, something failed, redisplay form
return View(model);
}
//
// POST: /Account/LogOff
[HttpPost]
[Route("LogOff")]
public async Task<IActionResult> LogOff()
{
await _signInManager.SignOutAsync();
_logger.LogInformation(4, "User logged out.");
//return RedirectToAction(nameof(HomeController.Index), "Home");
return Ok();
}
//
// POST: /Account/ExternalLogin
[HttpPost]
[AllowAnonymous]
[Route("ExternalLogin")]
public IActionResult ExternalLogin(string provider, string returnUrl = null)
{
// Request a redirect to the external login provider.
var redirectUrl = Url.Action("ExternalLoginCallback", "Account", new { ReturnUrl = returnUrl });
var properties = _signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl);
return Challenge(properties, provider);
}
//
// GET: /Account/ExternalLoginCallback
[HttpGet]
[AllowAnonymous]
[Route("ExternalLoginCallback")]
public async Task<IActionResult> ExternalLoginCallback(string returnUrl = null, string remoteError = null)
{
if (remoteError != null)
{
ModelState.AddModelError(string.Empty, $"Error from external provider: {remoteError}");
return View(nameof(Login));
}
var info = await _signInManager.GetExternalLoginInfoAsync();
if (info == null)
{
return RedirectToAction(nameof(Login));
}
// Sign in the user with this external login provider if the user already has a login.
var result = await _signInManager.ExternalLoginSignInAsync(info.LoginProvider, info.ProviderKey, isPersistent: false);
if (result.Succeeded)
{
// Update any authentication tokens if login succeeded
await _signInManager.UpdateExternalAuthenticationTokensAsync(info);
_logger.LogInformation(5, "User logged in with {Name} provider.", info.LoginProvider);
return RedirectToLocal(returnUrl);
}
if (result.RequiresTwoFactor)
{
return RedirectToAction(nameof(SendCode), new { ReturnUrl = returnUrl });
}
if (result.IsLockedOut)
{
return View("Lockout");
}
else
{
// If the user does not have an account, then ask the user to create an account.
ViewData["ReturnUrl"] = returnUrl;
ViewData["ProviderDisplayName"] = info.ProviderDisplayName;
var email = info.Principal.FindFirstValue(ClaimTypes.Email);
return View("ExternalLoginConfirmation", new ExternalLoginConfirmationViewModel { Email = email });
}
}
//
// POST: /Account/ExternalLoginConfirmation
[HttpPost]
[AllowAnonymous]
[Route("ExternalLoginConfirmation")]
public async Task<IActionResult> ExternalLoginConfirmation(ExternalLoginConfirmationViewModel model, string returnUrl = null)
{
if (ModelState.IsValid)
{
// Get the information about the user from the external login provider
var info = await _signInManager.GetExternalLoginInfoAsync();
if (info == null)
{
return View("ExternalLoginFailure");
}
var user = new ApplicationUser { UserName = model.Email, Email = model.Email };
var result = await _userManager.CreateAsync(user);
if (result.Succeeded)
{
result = await _userManager.AddLoginAsync(user, info);
if (result.Succeeded)
{
await _signInManager.SignInAsync(user, isPersistent: false);
_logger.LogInformation(6, "User created an account using {Name} provider.", info.LoginProvider);
// Update any authentication tokens as well
await _signInManager.UpdateExternalAuthenticationTokensAsync(info);
return RedirectToLocal(returnUrl);
}
}
AddErrors(result);
}
ViewData["ReturnUrl"] = returnUrl;
return View(model);
}
// GET: /Account/ConfirmEmail
[HttpGet]
[AllowAnonymous]
[Route("ConfirmEmail")]
public async Task<IActionResult> ConfirmEmail(string userId, string code)
{
if (userId == null || code == null)
{
return View("Error");
}
var user = await _userManager.FindByIdAsync(userId);
if (user == null)
{
return View("Error");
}
var result = await _userManager.ConfirmEmailAsync(user, code);
return View(result.Succeeded ? "ConfirmEmail" : "Error");
}
//
// GET: /Account/ForgotPassword
[HttpGet]
[AllowAnonymous]
[Route("ForgotPassword")]
public IActionResult ForgotPassword()
{
return View();
}
//
// POST: /Account/ForgotPassword
[HttpPost]
[AllowAnonymous]
[Route("ForgotPassword")]
public async Task<IActionResult> ForgotPassword(ForgotPasswordViewModel model)
{
if (ModelState.IsValid)
{
var user = await _userManager.FindByEmailAsync(model.Email);
if (user == null || !(await _userManager.IsEmailConfirmedAsync(user)))
{
// Don't reveal that the user does not exist or is not confirmed
return View("ForgotPasswordConfirmation");
}
// For more information on how to enable account confirmation and password reset please visit http://go.microsoft.com/fwlink/?LinkID=532713
// Send an email with this link
//var code = await _userManager.GeneratePasswordResetTokenAsync(user);
//var callbackUrl = Url.Action("ResetPassword", "Account", new { userId = user.Id, code = code }, protocol: HttpContext.Request.Scheme);
//await _emailSender.SendEmailAsync(model.Email, "Reset Password",
// "Please reset your password by clicking here: <a href=\"" + callbackUrl + "\">link</a>");
//return View("ForgotPasswordConfirmation");
}
// If we got this far, something failed, redisplay form
return View(model);
}
//
// GET: /Account/ForgotPasswordConfirmation
[HttpGet]
[AllowAnonymous]
[Route("ForgotPasswordConfirmation")]
public IActionResult ForgotPasswordConfirmation()
{
return View();
}
//
// GET: /Account/ResetPassword
[HttpGet]
[AllowAnonymous]
[Route("ResetPassword")]
public IActionResult ResetPassword(string code = null)
{
return code == null ? View("Error") : View();
}
//
// POST: /Account/ResetPassword
[HttpPost]
[AllowAnonymous]
[Route("ResetPassword")]
public async Task<IActionResult> ResetPassword(ResetPasswordViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
var user = await _userManager.FindByEmailAsync(model.Email);
if (user == null)
{
// Don't reveal that the user does not exist
return RedirectToAction(nameof(OldAccountController.ResetPasswordConfirmation), "Account");
}
var result = await _userManager.ResetPasswordAsync(user, model.Code, model.Password);
if (result.Succeeded)
{
return RedirectToAction(nameof(OldAccountController.ResetPasswordConfirmation), "Account");
}
AddErrors(result);
return View();
}
//
// GET: /Account/ResetPasswordConfirmation
[HttpGet]
[AllowAnonymous]
[Route("ResetPasswordConfirmation")]
public IActionResult ResetPasswordConfirmation()
{
return View();
}
//
// GET: /Account/SendCode
[HttpGet]
[AllowAnonymous]
[Route("SendCode")]
public async Task<ActionResult> SendCode(string returnUrl = null, bool rememberMe = false)
{
var user = await _signInManager.GetTwoFactorAuthenticationUserAsync();
if (user == null)
{
return View("Error");
}
var userFactors = await _userManager.GetValidTwoFactorProvidersAsync(user);
var factorOptions = userFactors.Select(purpose => new SelectListItem { Text = purpose, Value = purpose }).ToList();
return View(new SendCodeViewModel { Providers = factorOptions, ReturnUrl = returnUrl, RememberMe = rememberMe });
}
//
// POST: /Account/SendCode
[HttpPost]
[AllowAnonymous]
[Route("SendCode")]
public async Task<IActionResult> SendCode(SendCodeViewModel model)
{
if (!ModelState.IsValid)
{
return View();
}
var user = await _signInManager.GetTwoFactorAuthenticationUserAsync();
if (user == null)
{
return View("Error");
}
if (model.SelectedProvider == "Authenticator")
{
return RedirectToAction(nameof(VerifyAuthenticatorCode), new { ReturnUrl = model.ReturnUrl, RememberMe = model.RememberMe });
}
// Generate the token and send it
var code = await _userManager.GenerateTwoFactorTokenAsync(user, model.SelectedProvider);
if (string.IsNullOrWhiteSpace(code))
{
return View("Error");
}
var message = "Your security code is: " + code;
if (model.SelectedProvider == "Email")
{
await _emailSender.SendEmailAsync(await _userManager.GetEmailAsync(user), "Security Code", message);
}
// else if (model.SelectedProvider == "Phone")
// {
// await _smsSender.SendSmsAsync(await _userManager.GetPhoneNumberAsync(user), message);
// }
return RedirectToAction(nameof(VerifyCode), new { Provider = model.SelectedProvider, ReturnUrl = model.ReturnUrl, RememberMe = model.RememberMe });
}
//
// GET: /Account/VerifyCode
[HttpGet]
[AllowAnonymous]
[Route("VerifyCode")]
public async Task<IActionResult> VerifyCode(string provider, bool rememberMe, string returnUrl = null)
{
// Require that the user has already logged in via username/password or external login
var user = await _signInManager.GetTwoFactorAuthenticationUserAsync();
if (user == null)
{
return View("Error");
}
return View(new VerifyCodeViewModel { Provider = provider, ReturnUrl = returnUrl, RememberMe = rememberMe });
}
//
// POST: /Account/VerifyCode
[HttpPost]
[AllowAnonymous]
[Route("VerifyCode")]
public async Task<IActionResult> VerifyCode(VerifyCodeViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
// The following code protects for brute force attacks against the two factor codes.
// If a user enters incorrect codes for a specified amount of time then the user account
// will be locked out for a specified amount of time.
var result = await _signInManager.TwoFactorSignInAsync(model.Provider, model.Code, model.RememberMe, model.RememberBrowser);
if (result.Succeeded)
{
return RedirectToLocal(model.ReturnUrl);
}
if (result.IsLockedOut)
{
_logger.LogWarning(7, "User account locked out.");
return View("Lockout");
}
else
{
ModelState.AddModelError(string.Empty, "Invalid code.");
return View(model);
}
}
//
// GET: /Account/VerifyAuthenticatorCode
[HttpGet]
[AllowAnonymous]
[Route("VerifyAuthenticatorCode")]
public async Task<IActionResult> VerifyAuthenticatorCode(bool rememberMe, string returnUrl = null)
{
// Require that the user has already logged in via username/password or external login
var user = await _signInManager.GetTwoFactorAuthenticationUserAsync();
if (user == null)
{
return View("Error");
}
return View(new VerifyAuthenticatorCodeViewModel { ReturnUrl = returnUrl, RememberMe = rememberMe });
}
//
// POST: /Account/VerifyAuthenticatorCode
[HttpPost]
[AllowAnonymous]
[Route("VerifyAuthenticatorCode")]
public async Task<IActionResult> VerifyAuthenticatorCode(VerifyAuthenticatorCodeViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
// The following code protects for brute force attacks against the two factor codes.
// If a user enters incorrect codes for a specified amount of time then the user account
// will be locked out for a specified amount of time.
var result = await _signInManager.TwoFactorAuthenticatorSignInAsync(model.Code, model.RememberMe, model.RememberBrowser);
if (result.Succeeded)
{
return RedirectToLocal(model.ReturnUrl);
}
if (result.IsLockedOut)
{
_logger.LogWarning(7, "User account locked out.");
return View("Lockout");
}
else
{
ModelState.AddModelError(string.Empty, "Invalid code.");
return View(model);
}
}
//
// GET: /Account/UseRecoveryCode
[HttpGet]
[AllowAnonymous]
[Route("UseRecoveryCode")]
public async Task<IActionResult> UseRecoveryCode(string returnUrl = null)
{
// Require that the user has already logged in via username/password or external login
var user = await _signInManager.GetTwoFactorAuthenticationUserAsync();
if (user == null)
{
return View("Error");
}
return View(new UseRecoveryCodeViewModel { ReturnUrl = returnUrl });
}
//
// POST: /Account/UseRecoveryCode
[HttpPost]
[AllowAnonymous]
[Route("UseRecoveryCode")]
public async Task<IActionResult> UseRecoveryCode(UseRecoveryCodeViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
var result = await _signInManager.TwoFactorRecoveryCodeSignInAsync(model.Code);
if (result.Succeeded)
{
return RedirectToLocal(model.ReturnUrl);
}
else
{
ModelState.AddModelError(string.Empty, "Invalid code.");
return View(model);
}
}
#region Helpers
private void AddErrors(IdentityResult result)
{
foreach (var error in result.Errors)
{
ModelState.AddModelError(string.Empty, error.Description);
}
}
private Task<ApplicationUser> GetCurrentUserAsync()
{
return _userManager.GetUserAsync(HttpContext.User);
}
private IActionResult RedirectToLocal(string returnUrl)
{
if (Url.IsLocalUrl(returnUrl))
{
return Redirect(returnUrl);
}
else
{
return Redirect("/");
}
}
#endregion
}
}
@@ -0,0 +1,383 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using Authentication;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.UI.Services;
using Microsoft.AspNetCore.Mvc;
namespace gaseous_server.Controllers;
[ApiController]
[Route("Manage")]
[Authorize]
public class OldManageController : Controller
{
private readonly UserManager<ApplicationUser> _userManager;
private readonly SignInManager<ApplicationUser> _signInManager;
private readonly IEmailSender _emailSender;
private readonly ILogger _logger;
public OldManageController(
UserManager<ApplicationUser> userManager,
SignInManager<ApplicationUser> signInManager,
IEmailSender emailSender,
ILoggerFactory loggerFactory)
{
_userManager = userManager;
_signInManager = signInManager;
_emailSender = emailSender;
_logger = loggerFactory.CreateLogger<OldManageController>();
}
//
// GET: /Manage/Index
[HttpGet]
[Route("Index")]
public async Task<IActionResult> Index(ManageMessageId? message = null)
{
ViewData["StatusMessage"] =
message == ManageMessageId.ChangePasswordSuccess ? "Your password has been changed."
: message == ManageMessageId.SetPasswordSuccess ? "Your password has been set."
: message == ManageMessageId.SetTwoFactorSuccess ? "Your two-factor authentication provider has been set."
: message == ManageMessageId.Error ? "An error has occurred."
: message == ManageMessageId.AddPhoneSuccess ? "Your phone number was added."
: message == ManageMessageId.RemovePhoneSuccess ? "Your phone number was removed."
: "";
var user = await GetCurrentUserAsync();
var model = new IndexViewModel
{
HasPassword = await _userManager.HasPasswordAsync(user),
PhoneNumber = await _userManager.GetPhoneNumberAsync(user),
TwoFactor = await _userManager.GetTwoFactorEnabledAsync(user),
Logins = await _userManager.GetLoginsAsync(user),
BrowserRemembered = await _signInManager.IsTwoFactorClientRememberedAsync(user),
AuthenticatorKey = await _userManager.GetAuthenticatorKeyAsync(user)
};
return View(model);
}
//
// POST: /Manage/RemoveLogin
[HttpPost]
[Route("RemoveLogin")]
public async Task<IActionResult> RemoveLogin(RemoveLoginViewModel account)
{
ManageMessageId? message = ManageMessageId.Error;
var user = await GetCurrentUserAsync();
if (user != null)
{
var result = await _userManager.RemoveLoginAsync(user, account.LoginProvider, account.ProviderKey);
if (result.Succeeded)
{
await _signInManager.SignInAsync(user, isPersistent: false);
message = ManageMessageId.RemoveLoginSuccess;
}
}
return RedirectToAction(nameof(ManageLogins), new { Message = message });
}
//
// GET: /Manage/AddPhoneNumber
[HttpGet]
[Route("AddPhoneNumber")]
public IActionResult AddPhoneNumber()
{
return View();
}
//
// POST: /Manage/AddPhoneNumber
[HttpPost]
[Route("AddPhoneNumber")]
public async Task<IActionResult> AddPhoneNumber(AddPhoneNumberViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
// Generate the token and send it
var user = await GetCurrentUserAsync();
var code = await _userManager.GenerateChangePhoneNumberTokenAsync(user, model.PhoneNumber);
//await _smsSender.SendSmsAsync(model.PhoneNumber, "Your security code is: " + code);
return RedirectToAction(nameof(VerifyPhoneNumber), new { PhoneNumber = model.PhoneNumber });
}
//
// POST: /Manage/ResetAuthenticatorKey
[HttpPost]
[Route("ResetAuthenticatorKey")]
public async Task<IActionResult> ResetAuthenticatorKey()
{
var user = await GetCurrentUserAsync();
if (user != null)
{
await _userManager.ResetAuthenticatorKeyAsync(user);
_logger.LogInformation(1, "User reset authenticator key.");
}
return RedirectToAction(nameof(Index), "Manage");
}
//
// POST: /Manage/GenerateRecoveryCode
[HttpPost]
[Route("GenerateRecoveryCode")]
public async Task<IActionResult> GenerateRecoveryCode()
{
var user = await GetCurrentUserAsync();
if (user != null)
{
var codes = await _userManager.GenerateNewTwoFactorRecoveryCodesAsync(user, 5);
_logger.LogInformation(1, "User generated new recovery code.");
return View("DisplayRecoveryCodes", new DisplayRecoveryCodesViewModel { Codes = codes });
}
return View("Error");
}
//
// POST: /Manage/EnableTwoFactorAuthentication
[HttpPost]
[Route("EnableTwoFactorAuthentication")]
public async Task<IActionResult> EnableTwoFactorAuthentication()
{
var user = await GetCurrentUserAsync();
if (user != null)
{
await _userManager.SetTwoFactorEnabledAsync(user, true);
await _signInManager.SignInAsync(user, isPersistent: false);
_logger.LogInformation(1, "User enabled two-factor authentication.");
}
return RedirectToAction(nameof(Index), "Manage");
}
//
// POST: /Manage/DisableTwoFactorAuthentication
[HttpPost]
[Route("DisableTwoFactorAuthentication")]
public async Task<IActionResult> DisableTwoFactorAuthentication()
{
var user = await GetCurrentUserAsync();
if (user != null)
{
await _userManager.SetTwoFactorEnabledAsync(user, false);
await _signInManager.SignInAsync(user, isPersistent: false);
_logger.LogInformation(2, "User disabled two-factor authentication.");
}
return RedirectToAction(nameof(Index), "Manage");
}
//
// GET: /Manage/VerifyPhoneNumber
[HttpGet]
[Route("VerifyPhoneNumber")]
public async Task<IActionResult> VerifyPhoneNumber(string phoneNumber)
{
var code = await _userManager.GenerateChangePhoneNumberTokenAsync(await GetCurrentUserAsync(), phoneNumber);
// Send an SMS to verify the phone number
return phoneNumber == null ? View("Error") : View(new VerifyPhoneNumberViewModel { PhoneNumber = phoneNumber });
}
//
// POST: /Manage/VerifyPhoneNumber
[HttpPost]
[Route("VerifyPhoneNumber")]
public async Task<IActionResult> VerifyPhoneNumber(VerifyPhoneNumberViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
var user = await GetCurrentUserAsync();
if (user != null)
{
var result = await _userManager.ChangePhoneNumberAsync(user, model.PhoneNumber, model.Code);
if (result.Succeeded)
{
await _signInManager.SignInAsync(user, isPersistent: false);
return RedirectToAction(nameof(Index), new { Message = ManageMessageId.AddPhoneSuccess });
}
}
// If we got this far, something failed, redisplay the form
ModelState.AddModelError(string.Empty, "Failed to verify phone number");
return View(model);
}
//
// GET: /Manage/RemovePhoneNumber
[HttpPost]
[Route("RemovePhoneNumber")]
public async Task<IActionResult> RemovePhoneNumber()
{
var user = await GetCurrentUserAsync();
if (user != null)
{
var result = await _userManager.SetPhoneNumberAsync(user, null);
if (result.Succeeded)
{
await _signInManager.SignInAsync(user, isPersistent: false);
return RedirectToAction(nameof(Index), new { Message = ManageMessageId.RemovePhoneSuccess });
}
}
return RedirectToAction(nameof(Index), new { Message = ManageMessageId.Error });
}
//
// GET: /Manage/ChangePassword
[HttpGet]
[Route("ChangePassword")]
public IActionResult ChangePassword()
{
return View();
}
//
// POST: /Manage/ChangePassword
[HttpPost]
[Route("ChangePassword")]
public async Task<IActionResult> ChangePassword(ChangePasswordViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
var user = await GetCurrentUserAsync();
if (user != null)
{
var result = await _userManager.ChangePasswordAsync(user, model.OldPassword, model.NewPassword);
if (result.Succeeded)
{
await _signInManager.SignInAsync(user, isPersistent: false);
_logger.LogInformation(3, "User changed their password successfully.");
return RedirectToAction(nameof(Index), new { Message = ManageMessageId.ChangePasswordSuccess });
}
AddErrors(result);
return View(model);
}
return RedirectToAction(nameof(Index), new { Message = ManageMessageId.Error });
}
//
// GET: /Manage/SetPassword
[HttpGet]
[Route("SetPassword")]
public IActionResult SetPassword()
{
return View();
}
//
// POST: /Manage/SetPassword
[HttpPost]
[Route("SetPassword")]
public async Task<IActionResult> SetPassword(SetPasswordViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
var user = await GetCurrentUserAsync();
if (user != null)
{
var result = await _userManager.AddPasswordAsync(user, model.NewPassword);
if (result.Succeeded)
{
await _signInManager.SignInAsync(user, isPersistent: false);
return RedirectToAction(nameof(Index), new { Message = ManageMessageId.SetPasswordSuccess });
}
AddErrors(result);
return View(model);
}
return RedirectToAction(nameof(Index), new { Message = ManageMessageId.Error });
}
//GET: /Manage/ManageLogins
[HttpGet]
[Route("ManageLogins")]
public async Task<IActionResult> ManageLogins(ManageMessageId? message = null)
{
ViewData["StatusMessage"] =
message == ManageMessageId.RemoveLoginSuccess ? "The external login was removed."
: message == ManageMessageId.AddLoginSuccess ? "The external login was added."
: message == ManageMessageId.Error ? "An error has occurred."
: "";
var user = await GetCurrentUserAsync();
if (user == null)
{
return View("Error");
}
var userLogins = await _userManager.GetLoginsAsync(user);
var schemes = await _signInManager.GetExternalAuthenticationSchemesAsync();
var otherLogins = schemes.Where(auth => userLogins.All(ul => auth.Name != ul.LoginProvider)).ToList();
ViewData["ShowRemoveButton"] = user.PasswordHash != null || userLogins.Count > 1;
return View(new ManageLoginsViewModel
{
CurrentLogins = userLogins,
OtherLogins = otherLogins
});
}
//
// POST: /Manage/LinkLogin
[HttpPost]
[Route("LinkLogin")]
public IActionResult LinkLogin(string provider)
{
// Request a redirect to the external login provider to link a login for the current user
var redirectUrl = Url.Action("LinkLoginCallback", "Manage");
var properties = _signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl, _userManager.GetUserId(User));
return Challenge(properties, provider);
}
//
// GET: /Manage/LinkLoginCallback
[HttpGet]
[Route("LinkLoginCallback")]
public async Task<ActionResult> LinkLoginCallback()
{
var user = await GetCurrentUserAsync();
if (user == null)
{
return View("Error");
}
var info = await _signInManager.GetExternalLoginInfoAsync(await _userManager.GetUserIdAsync(user));
if (info == null)
{
return RedirectToAction(nameof(ManageLogins), new { Message = ManageMessageId.Error });
}
var result = await _userManager.AddLoginAsync(user, info);
var message = result.Succeeded ? ManageMessageId.AddLoginSuccess : ManageMessageId.Error;
return RedirectToAction(nameof(ManageLogins), new { Message = message });
}
#region Helpers
private void AddErrors(IdentityResult result)
{
foreach (var error in result.Errors)
{
ModelState.AddModelError(string.Empty, error.Description);
}
}
public enum ManageMessageId
{
AddPhoneSuccess,
AddLoginSuccess,
ChangePasswordSuccess,
SetTwoFactorSuccess,
SetPasswordSuccess,
RemoveLoginSuccess,
RemovePhoneSuccess,
Error
}
private Task<ApplicationUser> GetCurrentUserAsync()
{
return _userManager.GetUserAsync(HttpContext.User);
}
#endregion
}
@@ -0,0 +1,54 @@
CREATE TABLE `roles` (
`Id` varchar(128) NOT NULL,
`Name` varchar(256) NOT NULL,
PRIMARY KEY (`Id`)
);
CREATE TABLE `users` (
`Id` varchar(128) NOT NULL,
`Email` varchar(256) DEFAULT NULL,
`EmailConfirmed` tinyint(1) NOT NULL,
`NormalizedEmail` varchar(256) DEFAULT NULL,
`PasswordHash` longtext,
`SecurityStamp` longtext,
`ConcurrencyStamp` longtext,
`PhoneNumber` longtext,
`PhoneNumberConfirmed` tinyint(1) NOT NULL,
`TwoFactorEnabled` tinyint(1) NOT NULL,
`LockoutEnd` datetime DEFAULT NULL,
`LockoutEnabled` tinyint(1) NOT NULL,
`AccessFailedCount` int(11) NOT NULL,
`UserName` varchar(256) NOT NULL,
`NormalizedUserName` varchar(256) NOT NULL,
`SecurityProfile` longtext,
PRIMARY KEY (`Id`)
);
CREATE TABLE `userclaims` (
`Id` int(11) NOT NULL AUTO_INCREMENT,
`UserId` varchar(128) NOT NULL,
`ClaimType` longtext,
`ClaimValue` longtext,
PRIMARY KEY (`Id`),
UNIQUE KEY `Id` (`Id`),
KEY `UserId` (`UserId`),
CONSTRAINT `ApplicationUser_Claims` FOREIGN KEY (`UserId`) REFERENCES `users` (`Id`) ON DELETE CASCADE ON UPDATE NO ACTION
);
CREATE TABLE `userlogins` (
`LoginProvider` varchar(128) NOT NULL,
`ProviderKey` varchar(128) NOT NULL,
`UserId` varchar(128) NOT NULL,
PRIMARY KEY (`LoginProvider`,`ProviderKey`,`UserId`),
KEY `ApplicationUser_Logins` (`UserId`),
CONSTRAINT `ApplicationUser_Logins` FOREIGN KEY (`UserId`) REFERENCES `users` (`Id`) ON DELETE CASCADE ON UPDATE NO ACTION
);
CREATE TABLE `userroles` (
`UserId` varchar(128) NOT NULL,
`RoleId` varchar(128) NOT NULL,
PRIMARY KEY (`UserId`,`RoleId`),
KEY `IdentityRole_Users` (`RoleId`),
CONSTRAINT `ApplicationUser_Roles` FOREIGN KEY (`UserId`) REFERENCES `users` (`Id`) ON DELETE CASCADE ON UPDATE NO ACTION,
CONSTRAINT `IdentityRole_Users` FOREIGN KEY (`RoleId`) REFERENCES `roles` (`Id`) ON DELETE CASCADE ON UPDATE NO ACTION
) ;
+5
View File
@@ -20,9 +20,11 @@
<ItemGroup>
<PackageReference Include="gaseous-signature-parser" Version="2.0.0" />
<PackageReference Include="gaseous.IGDB" Version="1.0.1" />
<PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="7.0.13" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Versioning" Version="5.1.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Versioning.ApiExplorer" Version="5.1.0" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="7.0.12" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="7.0.10" />
<PackageReference Include="MySqlConnector" Version="2.2.7" />
@@ -43,6 +45,7 @@
<None Remove="Support\Database\MySQL\gaseous-1002.sql" />
<None Remove="Support\Database\MySQL\gaseous-1003.sql" />
<None Remove="Support\Database\MySQL\gaseous-1004.sql" />
<None Remove="Support\Database\MySQL\gaseous-1005.sql" />
<None Remove="Classes\Metadata\" />
<None Remove="Assets\" />
<None Remove="Assets\Ratings\" />
@@ -114,6 +117,7 @@
<Folder Include="Assets\Ratings\USK\" />
<Folder Include="Assets\Ratings\GRAC\" />
<Folder Include="Assets\Ratings\CLASS_IND\" />
<Folder Remove="Reference" />
</ItemGroup>
<ItemGroup>
<None Include="wwwroot\**">
@@ -169,5 +173,6 @@
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1002.sql" />
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1003.sql" />
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1004.sql" />
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1005.sql" />
</ItemGroup>
</Project>
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 592 KiB

+5
View File
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" version="1.1" fill="none" stroke="#000000" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5">
<path d="m14.25 8.75c-.5 2.5-2.3849 4.85363-5.03069 5.37991-2.64578.5263-5.33066-.7044-6.65903-3.0523-1.32837-2.34784-1.00043-5.28307.81336-7.27989 1.81379-1.99683 4.87636-2.54771 7.37636-1.54771"/>
<polyline points="5.75 7.75,8.25 10.25,14.25 3.75"/>
</svg>

After

Width:  |  Height:  |  Size: 579 B

+5
View File
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M16 7C16 9.20914 14.2091 11 12 11C9.79086 11 8 9.20914 8 7C8 4.79086 9.79086 3 12 3C14.2091 3 16 4.79086 16 7Z" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M12 14C8.13401 14 5 17.134 5 21H19C19 17.134 15.866 14 12 14Z" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 590 B

+93 -24
View File
@@ -2,7 +2,7 @@
<html>
<head>
<meta charset="utf-8" />
<script src="/api/v1.0/System/VersionFile"></script>
<script src="/api/v1.1/System/VersionFile"></script>
<link type="text/css" rel="stylesheet" dat-href="/styles/style.css" />
<script src="/scripts/jquery-3.6.0.min.js"></script>
<script src="/scripts/moment.js"></script>
@@ -38,6 +38,8 @@
head.appendChild(newLink);
}
}
var userProfile;
</script>
</head>
<body>
@@ -45,24 +47,35 @@
<img src="/images/logo.png" alt="Gaseous" id="banner_icon_image" />
</div>
<div id="banner_header">
<div id="banner_cog" onclick="window.location.href = '/index.html?page=settings';" class="banner_button">
<img src="/images/settings.svg" alt="Settings" title="Settings" id="banner_system_image" class="banner_button_image" />
<span id="banner_system_label">Settings</span>
</div>
<div id="bannerButtons">
<div id="banner_user" onclick="showMenu();" class="banner_button dropdown dropbtn">
<img src="/images/user.svg" alt="Account" title="Account" id="banner_user_image" class="banner_button_image" style="position: relative; top: 10px; right: 0px; pointer-events: none;" onclick="showMenu();" />
<div id="myDropdown" class="dropdown-content">
<div id="banner_user_roles"></div>
<a href="#" onclick="showDialog('userprofile');">Profile</a>
<a href="#" onclick="userLogoff();">Sign Out</a>
</div>
</div>
<div id="banner_cog" onclick="window.location.href = '/index.html?page=settings';" class="banner_button">
<img src="/images/settings.svg" alt="Settings" title="Settings" id="banner_system_image" class="banner_button_image" />
<span id="banner_system_label">Settings</span>
</div>
<div id="banner_upload" onclick="showDialog('upload');" class="banner_button">
<img src="/images/upload.svg" alt="Upload" title="Upload" id="banner_upload_image" class="banner_button_image" />
<span id="banner_upload_label">Upload</span>
</div>
<div id="banner_upload" onclick="showDialog('upload');" class="banner_button">
<img src="/images/upload.svg" alt="Upload" title="Upload" id="banner_upload_image" class="banner_button_image" />
<span id="banner_upload_label">Upload</span>
</div>
<div id="banner_collections" onclick="window.location.href = '/index.html?page=collections';" class="banner_button">
<img src="/images/collections.svg" alt="Collections" title="Collections" id="banner_collections_image" class="banner_button_image" />
<span id="banner_collections_label">Collections</span>
</div>
<div id="banner_collections" onclick="window.location.href = '/index.html?page=collections';" class="banner_button">
<img src="/images/collections.svg" alt="Collections" title="Collections" id="banner_collections_image" class="banner_button_image" />
<span id="banner_collections_label">Collections</span>
</div>
<div id="banner_library" onclick="window.location.href = '/index.html';" class="banner_button">
<img src="/images/library.svg" alt="Library" title="Library" id="banner_library_image" class="banner_button_image" />
<span id="banner_library_label">Library</span>
<div id="banner_library" onclick="window.location.href = '/index.html';" class="banner_button">
<img src="/images/library.svg" alt="Library" title="Library" id="banner_library_image" class="banner_button_image" />
<span id="banner_library_label">Library</span>
</div>
</div>
<div id="banner_header_label" onclick="window.location.href = '/index.html';">Gaseous Games</div>
@@ -96,18 +109,74 @@
</div>
<script type="text/javascript">var modalVariables = null;
<script type="text/javascript">
var modalVariables = null;
$(document).ready(function () {
var myParam = getQueryString('page', 'string');
// redirect if first run status = 0
if (FirstRunStatus == 0) {
window.location.replace("/pages/first.html");
}
if (!myParam) {
myParam = 'home';
// redirect if not logged in
ajaxCall(
'/api/v1.1/Account/Profile/Basic',
'GET',
function(result) {
console.log("User is logged in");
userProfile = result;
// hide the upload button if it's not permitted
var uploadButton = document.getElementById('banner_upload');
if (!userProfile.roles.includes("Admin") && !userProfile.roles.includes("Gamer")) {
uploadButton.style.display = 'none';
}
// populate page
var myParam = getQueryString('page', 'string');
if (!myParam) {
myParam = 'home';
}
$('#content').load('/pages/' + myParam + '.html?v=' + AppVersion);
},
function(error) {
window.location.replace("/pages/login.html");
}
);
$('#content').load('/pages/' + myParam + '.html?v=' + AppVersion);
});
/* When the user clicks on the button,
toggle between hiding and showing the dropdown content */
function showMenu() {
document.getElementById("myDropdown").classList.toggle("show");
}
// Close the dropdown menu if the user clicks outside of it
window.onclick = function(event) {
if (!event.target.matches('.dropbtn')) {
var dropdowns = document.getElementsByClassName("dropdown-content");
var i;
for (i = 0; i < dropdowns.length; i++) {
var openDropdown = dropdowns[i];
if (openDropdown.classList.contains('show')) {
openDropdown.classList.remove('show');
}
}
}
}
function userLogoff() {
ajaxCall(
'/api/v1.1/Account/LogOff',
'POST',
function (result) {
location.replace("/index.html");
},
function (error) {
location.replace("/index.html");
}
);
}
</script>
</body>
</html>
+16 -4
View File
@@ -17,10 +17,17 @@
</div>
<script type="text/javascript">
var newCollectionButton = document.getElementById('collection_new');
if (userProfile.roles.includes("Admin") || userProfile.roles.includes("Gamer")) {
newCollectionButton.style.display = '';
} else {
newCollectionButton.style.display = 'none';
}
GetCollections();
function GetCollections() {
ajaxCall('/api/v1.0/Collections', 'GET', function (result) {
ajaxCall('/api/v1.1/Collections', 'GET', function (result) {
if (result) {
var targetDiv = document.getElementById('collection_table_location');
targetDiv.innerHTML = '';
@@ -47,7 +54,7 @@
break;
case "Completed":
statusText = 'Available';
downloadLink = '<a href="/api/v1.0/Collections/' + result[i].id + '/Roms/Zip" class="romlink"><img src="/images/download.svg" class="banner_button_image" alt="Download" title="Download" /></a>';
downloadLink = '<a href="/api/v1.1/Collections/' + result[i].id + '/Roms/Zip" class="romlink"><img src="/images/download.svg" class="banner_button_image" alt="Download" title="Download" /></a>';
packageSize = formatBytes(result[i].collectionBuiltSizeBytes);
break;
case "Failed":
@@ -58,9 +65,14 @@
break;
}
var editButton = '<a href="#" onclick="showDialog(\'collectionedit\', ' + result[i].id + ');" class="romlink"><img src="/images/edit.svg" class="banner_button_image" alt="Edit" title="Edit" /></a>';
var editButton = '';
var deleteButton = '';
if (userProfile.roles.includes("Admin") || userProfile.roles.includes("Gamer")) {
editButton = '<a href="#" onclick="showDialog(\'collectionedit\', ' + result[i].id + ');" class="romlink"><img src="/images/edit.svg" class="banner_button_image" alt="Edit" title="Edit" /></a>';
var deleteButton = '<a href="#" onclick="showSubDialog(\'collectiondelete\', ' + result[i].id + ');" class="romlink"><img src="/images/delete.svg" class="banner_button_image" alt="Delete" title="Delete" /></a>';
deleteButton = '<a href="#" onclick="showSubDialog(\'collectiondelete\', ' + result[i].id + ');" class="romlink"><img src="/images/delete.svg" class="banner_button_image" alt="Delete" title="Delete" /></a>';
}
var newRow = [
result[i].name,
@@ -24,7 +24,7 @@
$('#collection_addgame').select2({
ajax: {
url: '/api/v1.0/Collections',
url: '/api/v1.1/Collections',
placeholder: 'Select collection',
processResults: function (data) {
var arr = [];
@@ -59,7 +59,7 @@
if (CollectionId != 0) {
ajaxCall(
'/api/v1.0/Collections/' + CollectionId + '/AlwaysInclude' + RebuildCollection,
'/api/v1.1/Collections/' + CollectionId + '/AlwaysInclude' + RebuildCollection,
'PATCH',
function (result) {
closeSubDialog();
@@ -12,7 +12,7 @@
<script type="text/javascript">
function deleteCollection() {
ajaxCall(
'/api/v1.0/Collections/' + subModalVariables,
'/api/v1.1/Collections/' + subModalVariables,
'DELETE',
function (result) {
GetCollections();
@@ -136,7 +136,7 @@
// setup dropdowns
$('#collection_platform').select2({
ajax: {
url: '/api/v1.0/Filter',
url: '/api/v1.1/Filter',
processResults: function (data) {
var filter = data['platforms'];
@@ -159,7 +159,7 @@
$('#collection_genres').select2({
ajax: {
url: '/api/v1.0/Filter',
url: '/api/v1.1/Filter',
processResults: function (data) {
var filter = data['genres'];
@@ -182,7 +182,7 @@
$('#collection_players').select2({
ajax: {
url: '/api/v1.0/Filter',
url: '/api/v1.1/Filter',
processResults: function (data) {
var filter = data['gamemodes'];
@@ -205,7 +205,7 @@
$('#collection_playerperspectives').select2({
ajax: {
url: '/api/v1.0/Filter',
url: '/api/v1.1/Filter',
processResults: function (data) {
var filter = data['playerperspectives'];
@@ -228,7 +228,7 @@
$('#collection_themes').select2({
ajax: {
url: '/api/v1.0/Filter',
url: '/api/v1.1/Filter',
processResults: function (data) {
var filter = data['themes'];
@@ -258,7 +258,7 @@
// edit mode
ajaxCall(
'/api/v1.0/Collections/' + modalVariables,
'/api/v1.1/Collections/' + modalVariables,
'GET',
function(result) {
if (result.name) { document.getElementById('collection_name').value = result.name; }
@@ -278,7 +278,7 @@
// fill select2 controls
$.ajax(
{
url: '/api/v1.0/Filter',
url: '/api/v1.1/Filter',
type: 'GET',
indexValue: result,
dataType: 'json',
@@ -344,7 +344,7 @@
// existing object - save over the top
item.id = modalVariables;
ajaxCall(
'/api/v1.0/Collections/' + modalVariables,
'/api/v1.1/Collections/' + modalVariables,
'PATCH',
function(result) {
location.reload();
@@ -357,7 +357,7 @@
} else {
// new object
ajaxCall(
'/api/v1.0/Collections',
'/api/v1.1/Collections',
'POST',
function(result) {
location.reload();
@@ -426,7 +426,7 @@
var item = GenerateCollectionItem();
ajaxCall(
'/api/v1.0/Collections/Preview',
'/api/v1.1/Collections/Preview',
'POST',
function(result) {
DisplayPreview(result, 'collectionedit_previewbox_content');
@@ -579,7 +579,7 @@
var gameImage = document.createElement('img');
gameImage.className = 'game_tile_image game_tile_image_small';
if (gameItem.cover) {
gameImage.src = '/api/v1.0/Games/' + gameItem.id + '/cover/image';
gameImage.src = '/api/v1.1/Games/' + gameItem.id + '/cover/image';
} else {
gameImage.src = '/images/unknowngame.png';
gameImage.className = 'game_tile_image game_tile_image_small unknown';
@@ -12,7 +12,7 @@
<script type="text/javascript">
function deleteLibrary() {
ajaxCall(
'/api/v1.0/Library/' + subModalVariables,
'/api/v1.1/Library/' + subModalVariables,
'DELETE',
function (result) {
drawLibrary();
@@ -32,7 +32,7 @@
$('#newlibrary_defaultplatform').select2({
minimumInputLength: 3,
ajax: {
url: '/api/v1.0/Search/Platform',
url: '/api/v1.1/Search/Platform',
data: function (params) {
var query = {
SearchString: params.term
@@ -76,7 +76,7 @@
alert("A path must be provided.");
} else {
ajaxCall(
'/api/v1.0/Library?Name=' + encodeURIComponent(libName) + '&DefaultPlatformId=' + libPlatform[0].id + '&Path=' + encodeURIComponent(libPath),
'/api/v1.1/Library?Name=' + encodeURIComponent(libName) + '&DefaultPlatformId=' + libPlatform[0].id + '&Path=' + encodeURIComponent(libPath),
'POST',
function(result) {
drawLibrary();
@@ -12,7 +12,7 @@
<script type="text/javascript">
function deleteCollection() {
ajaxCall(
'/api/v1.0/Games/' + gameData.id + '/romgroup/' + subModalVariables,
'/api/v1.1/Games/' + gameData.id + '/romgroup/' + subModalVariables,
'DELETE',
function (result) {
loadRoms();
@@ -105,7 +105,7 @@
DisplayWebEmulatorContent(false);
ajaxCall(
'/api/v1.0/PlatformMaps/' + modalVariables,
'/api/v1.1/PlatformMaps/' + modalVariables,
'GET',
function (result) {
// set heading
@@ -338,7 +338,7 @@
console.log(JSON.stringify(item));
ajaxCall(
'/api/v1.0/PlatformMaps/' + modalVariables,
'/api/v1.1/PlatformMaps/' + modalVariables,
'PATCH',
function (result) {
loadPlatformMapping();
@@ -11,7 +11,7 @@
<script type="text/javascript">
function deleteRom() {
ajaxCall('/api/v1.0/Games/' + gameId + '/roms/' + modalVariables, 'DELETE', function (result) {
ajaxCall('/api/v1.1/Games/' + gameId + '/roms/' + modalVariables, 'DELETE', function (result) {
window.location.reload();
});
}
@@ -118,7 +118,7 @@
}
}
ajaxCall('/api/v1.0/Games/' + gameId + '/roms/' + modalVariables, 'GET', function (result) {
ajaxCall('/api/v1.1/Games/' + gameId + '/roms/' + modalVariables, 'GET', function (result) {
romData = result;
document.getElementById('modal-heading').innerHTML = result.name;
document.getElementById('rominfo_library').innerHTML = result.library.name;
@@ -169,7 +169,7 @@
$('#properties_fixplatform').select2({
minimumInputLength: 3,
ajax: {
url: '/api/v1.0/Search/Platform',
url: '/api/v1.1/Search/Platform',
data: function (params) {
var query = {
SearchString: params.term
@@ -200,7 +200,7 @@
minimumInputLength: 3,
templateResult: DropDownRenderGameOption,
ajax: {
url: '/api/v1.0/Search/Game',
url: '/api/v1.1/Search/Game',
data: function (params) {
fixplatform = $('#properties_fixplatform').select2('data');
@@ -236,7 +236,7 @@
document.getElementById('properties_fixsave').setAttribute("disabled", "disabled");
ajaxCall('/api/v1.0/Games/' + gameId + '/roms/' + modalVariables + '?NewPlatformId=' + fixplatform[0].id + '&NewGameId=' + fixgame[0].id, 'PATCH', function (result) {
ajaxCall('/api/v1.1/Games/' + gameId + '/roms/' + modalVariables + '?NewPlatformId=' + fixplatform[0].id + '&NewGameId=' + fixgame[0].id, 'PATCH', function (result) {
window.location.reload();
});
}
@@ -0,0 +1,29 @@
<p>Are you sure you want to delete the selected user?</p>
<p><strong>Warning:</strong> This cannot be undone!</p>
<div style="width: 100%; text-align: center;">
<div style="display: inline-block; margin-right: 20px;">
<button class="redbutton" value="Delete" onclick="deleteUserCallback();">Delete</button>
</div>
<div style="display: inline-block; margin-left: 20px;">
<button value="Cancel" onclick="closeSubDialog();">Cancel</button>
</div>
</div>
<script type="text/javascript">
function deleteUserCallback() {
ajaxCall(
'/api/v1.1/Account/Users/' + subModalVariables,
'DELETE',
function(result) {
console.log('Deleted user');
GetUsers();
closeSubDialog();
},
function(error) {
console.log(JSON.stringify(error));
GetUsers();
closeSubDialog();
}
);
}
</script>
@@ -0,0 +1,375 @@
<div id="properties_toc">
<div id="properties_user_toc_password" name="properties_user_toc_item" onclick="UserSelectTab('password');">Password</div>
<div id="properties_user_toc_role" name="properties_user_toc_item" onclick="UserSelectTab('role');">Role</div>
<div id="properties_user_toc_age" name="properties_user_toc_item" onclick="UserSelectTab('age');">Content Restrictions</div>
<!--<div id="properties_toc_manage" name="properties_toc_item" onclick="SelectTab('manage');">Manage</div>-->
</div>
<div id="properties_bodypanel" style="height: 450px; overflow-y: scroll;">
<div id="properties_bodypanel_password" name="properties_user_tab" style="display: none;">
<table style="width: 100%;">
<tr>
<th>
Password
</th>
<td>
<input type="password" id="settings_users_edit_password" style="width: 95%;" onkeyup="checkPasswordsMatch();" />
</td>
</tr>
<tr>
<th>
Confirm password
</th>
<td>
<input type="password" id="settings_users_edit_confirmpassword" style="width: 95%;" onkeyup="checkPasswordsMatch();" />
</td>
</tr>
<tr>
<td colspan="2" id="settings_users_edit_label"></td>
</tr>
</table>
</div>
<div id="properties_bodypanel_role" name="properties_user_tab" style="display: none;">
<table style="width: 100%;" class="romtable">
<tr>
<th>
</th>
<th>
<input type="radio" name="settings_user_role" id="settings_user_role_player" value="Player"> <label for="settings_user_role_player">Player</label>
</th>
<th>
<input type="radio" name="settings_user_role" id="settings_user_role_gamer" value="Gamer"> <label for="settings_user_role_gamer">Gamer</label>
</th>
<th>
<input type="radio" name="settings_user_role" id="settings_user_role_admin" value="Admin"> <label for="settings_user_role_admin">Administrator</label>
</th>
</tr>
<tr>
<th colspan="4"><h3>Games and ROM's</h3></td>
</tr>
<tr class="romrow">
<td class="romcell">Play games</td>
<td class="romcell"><img src="/images/tick.svg" class="banner_button_image" /></td>
<td class="romcell"><img src="/images/tick.svg" class="banner_button_image" /></td>
<td class="romcell"><img src="/images/tick.svg" class="banner_button_image" /></td>
</tr>
<tr class="romrow">
<td class="romcell">Download ROM images</td>
<td class="romcell"><img src="/images/tick.svg" class="banner_button_image" /></td>
<td class="romcell"><img src="/images/tick.svg" class="banner_button_image" /></td>
<td class="romcell"><img src="/images/tick.svg" class="banner_button_image" /></td>
</tr>
<tr class="romrow">
<td class="romcell">Upload ROM images</td>
<td class="romcell"></td>
<td class="romcell"><img src="/images/tick.svg" class="banner_button_image" /></td>
<td class="romcell"><img src="/images/tick.svg" class="banner_button_image" /></td>
</tr>
<tr class="romrow">
<td class="romcell">Create and delete multidisk packages</td>
<td class="romcell"></td>
<td class="romcell"><img src="/images/tick.svg" class="banner_button_image" /></td>
<td class="romcell"><img src="/images/tick.svg" class="banner_button_image" /></td>
</tr>
<tr class="romrow">
<td class="romcell">Delete ROM images</td>
<td class="romcell"></td>
<td class="romcell"><img src="/images/tick.svg" class="banner_button_image" /></td>
<td class="romcell"><img src="/images/tick.svg" class="banner_button_image" /></td>
</tr>
<tr class="romrow">
<td class="romcell">Fix ROM image matches</td>
<td class="romcell"></td>
<td class="romcell"><img src="/images/tick.svg" class="banner_button_image" /></td>
<td class="romcell"><img src="/images/tick.svg" class="banner_button_image" /></td>
</tr>
<tr>
<th colspan="4"><h3>Collections</h3></td>
</tr>
<tr class="romrow">
<td class="romcell">Download packages</td>
<td class="romcell"><img src="/images/tick.svg" class="banner_button_image" /></td>
<td class="romcell"><img src="/images/tick.svg" class="banner_button_image" /></td>
<td class="romcell"><img src="/images/tick.svg" class="banner_button_image" /></td>
</tr>
<tr class="romrow">
<td class="romcell">Create packages</td>
<td class="romcell"></td>
<td class="romcell"><img src="/images/tick.svg" class="banner_button_image" /></td>
<td class="romcell"><img src="/images/tick.svg" class="banner_button_image" /></td>
</tr>
<tr class="romrow">
<td class="romcell">Edit packages</td>
<td class="romcell"></td>
<td class="romcell"><img src="/images/tick.svg" class="banner_button_image" /></td>
<td class="romcell"><img src="/images/tick.svg" class="banner_button_image" /></td>
</tr>
<tr class="romrow">
<td class="romcell">Delete packages</td>
<td class="romcell"></td>
<td class="romcell"><img src="/images/tick.svg" class="banner_button_image" /></td>
<td class="romcell"><img src="/images/tick.svg" class="banner_button_image" /></td>
</tr>
<tr>
<th colspan="4"><h3>Settings</h3></td>
</tr>
<tr class="romrow">
<td class="romcell">Download firmware</td>
<td class="romcell"><img src="/images/tick.svg" class="banner_button_image" /></td>
<td class="romcell"><img src="/images/tick.svg" class="banner_button_image" /></td>
<td class="romcell"><img src="/images/tick.svg" class="banner_button_image" /></td>
</tr>
<tr class="romrow">
<td class="romcell">View background tasks</td>
<td class="romcell"><img src="/images/tick.svg" class="banner_button_image" /></td>
<td class="romcell"><img src="/images/tick.svg" class="banner_button_image" /></td>
<td class="romcell"><img src="/images/tick.svg" class="banner_button_image" /></td>
</tr>
<tr class="romrow">
<td class="romcell">View platform mapping</td>
<td class="romcell"><img src="/images/tick.svg" class="banner_button_image" /></td>
<td class="romcell"><img src="/images/tick.svg" class="banner_button_image" /></td>
<td class="romcell"><img src="/images/tick.svg" class="banner_button_image" /></td>
</tr>
<tr class="romrow">
<td class="romcell">Modify platform mapping</td>
<td class="romcell"></td>
<td class="romcell"></td>
<td class="romcell"><img src="/images/tick.svg" class="banner_button_image" /></td>
</tr>
<tr class="romrow">
<td class="romcell">Manually start background tasks</td>
<td class="romcell"></td>
<td class="romcell"></td>
<td class="romcell"><img src="/images/tick.svg" class="banner_button_image" /></td>
</tr>
<tr class="romrow">
<td class="romcell">View logs</td>
<td class="romcell"></td>
<td class="romcell"></td>
<td class="romcell"><img src="/images/tick.svg" class="banner_button_image" /></td>
</tr>
<tr class="romrow">
<td class="romcell">Manage user accounts</td>
<td class="romcell"></td>
<td class="romcell"></td>
<td class="romcell"><img src="/images/tick.svg" class="banner_button_image" /></td>
</tr>
</table>
</div>
<div id="properties_bodypanel_age" name="properties_user_tab" style="display: none;">
<h3>Classification Restrictions</h3>
<div id="properties_bodypanel_age_classification">
</div>
<input type="checkbox" id="properties_bodypanel_age_includeunclassified"> <label for="properties_bodypanel_age_includeunclassified">Include unclassified titles</label>
</div>
</div>
<div style="width: 100%; padding-top: 10px; text-align: right;">
<button id="settings_users_edit_okbutton" value="OK" onclick="saveProperties();">OK</button>
</div>
<script type="text/javascript">
ajaxCall(
'/api/v1.1/Account/users/' + modalVariables,
'GET',
function(result) {
document.getElementById('modal-heading').innerHTML = result.emailAddress;
// role page
document.getElementById('settings_user_role_' + result.highestRole.toLowerCase()).checked = true;
// age restriction page
var ageRatingsContainer = document.getElementById('properties_bodypanel_age_classification');
var ageRatingsTable = document.createElement('table');
ageRatingsTable.style.width = '100%';
ageRatingsTable.cellSpacing = 0;
ageRatingsTable.appendChild(
createTableRow(
true,
[
'Highest Allowed Rating',
'Included Ratings'
],
'',
''
)
);
for (var ageGroup in AgeRatingGroups) {
if (AgeRatingGroups.hasOwnProperty(ageGroup)) {
var ratingsValues = '';
var classBoards = AgeRatingGroups[ageGroup];
for (var classBoard in classBoards) {
for (var rating in classBoards[classBoard]) {
ratingsValues += "<img src='/api/v1.1/Ratings/Images/" + classBoard + "/" + classBoards[classBoard][rating] + "/image.svg' class='rating_image_mini' />";
}
}
var radioCheckedState = '';
if (result.securityProfile.ageRestrictionPolicy.maximumAgeRestriction.toLowerCase() == ageGroup.toLocaleLowerCase()) {
radioCheckedState = "checked='checked'";
}
ageRatingsTable.appendChild(
createTableRow(
false,
[
"<input type='radio' id='properties_bodypanel_age_classification_" + ageGroup + "' name='classification_group' " + radioCheckedState + " value='" + ageGroup + "' /> <label for='properties_bodypanel_age_classification_" + ageGroup + "'>" + ageGroup + "</label>",
ratingsValues
],
'romrow',
'romcell'
)
);
}
}
ageRatingsContainer.appendChild(ageRatingsTable);
document.getElementById('properties_bodypanel_age_includeunclassified').checked = result.securityProfile.ageRestrictionPolicy.includeUnrated;
},
function(error) {
closeDialog();
}
);
function UserSelectTab(TabName) {
var tabs = document.getElementsByName('properties_user_tab');
for (var i = 0; i < tabs.length; i++) {
if ((tabs[i].id) == ("properties_bodypanel_" + TabName)) {
tabs[i].style.display = '';
} else {
tabs[i].style.display = 'none';
}
}
var tocs = document.getElementsByName('properties_user_toc_item');
for (var i = 0; i < tocs.length; i++) {
if ((tocs[i].id) == ("properties_user_toc_" + TabName)) {
tocs[i].className = "properties_toc_item_selected";
} else {
tocs[i].className = '';
}
}
}
function checkPasswordsMatch() {
var newPassword = document.getElementById('settings_users_edit_password').value;
var conPassword = document.getElementById('settings_users_edit_confirmpassword').value;
var errorLabel = document.getElementById('settings_users_edit_label');
var submitButton = document.getElementById('settings_users_edit_okbutton');
if (newPassword.length > 0) {
if (newPassword == conPassword) {
// check if password meets requirements
if (newPassword.length > 10) {
errorLabel.innerHTML = "";
submitButton.removeAttribute('disabled');
return true;
} else {
errorLabel.innerHTML = "Password should be at least 10 characters long";
submitButton.setAttribute('disabled', 'disabled');
return false;
}
} else {
errorLabel.innerHTML = "New and confirmed passwords do not match";
submitButton.setAttribute('disabled', 'disabled');
return false;
}
} else {
errorLabel.innerHTML = "Password will not be changed";
submitButton.removeAttribute('disabled');
return true;
}
}
function saveProperties() {
saveRole();
}
function saveRole() {
if (checkPasswordsMatch() == true) {
// process role
var selectedRole = $("input[type='radio'][name='settings_user_role']:checked").val();
ajaxCall(
'/api/v1.1/Account/users/' + modalVariables + '/Roles?RoleName=' + selectedRole,
'POST',
function(result) {
saveSecurityProfile();
},
function(error) {
saveSecurityProfile();
}
);
}
}
function saveSecurityProfile() {
if (checkPasswordsMatch() == true) {
// process security profile
var securityProfile = {
"ageRestrictionPolicy": {
"maximumAgeRestriction": $("input[type='radio'][name='classification_group']:checked").val(),
"includeUnrated": document.getElementById('properties_bodypanel_age_includeunclassified').checked
}
};
ajaxCall(
'/api/v1.1/Account/users/' + modalVariables + '/SecurityProfile',
'POST',
function(result) {
savePassword();
},
function(error) {
savePassword();
},
JSON.stringify(securityProfile)
);
}
}
function savePassword() {
console.log("Save Password");
if (checkPasswordsMatch() == true) {
console.log("Passwords match");
var newPassword = document.getElementById('settings_users_edit_password').value;
if (newPassword.length > 0) {
var model = {
"newPassword": newPassword,
"confirmPassword": newPassword
}
ajaxCall(
'/api/v1.1/Account/Users/' + modalVariables + '/Password',
'POST',
function(result) {
console.log(JSON.stringify(result));
savePropertiesCallback();
},
function(error) {
console.log(JSON.stringify(error));
savePropertiesCallback();
},
JSON.stringify(model)
);
} else {
console.log("Password not long enough to change");
savePropertiesCallback();
}
} else {
console.log("Passwords don't match");
}
}
function savePropertiesCallback() {
GetUsers();
closeDialog();
}
UserSelectTab('password');
</script>
@@ -0,0 +1,88 @@
<h3>New User</h3>
<table style="width: 100%;">
<tr>
<th>
Email
</th>
<td>
<input type="text" id="settings_users_new_username" style="width: 95%;" onkeyup="checkPasswordsMatch();" />
</td>
</tr>
<tr>
<th>
Password
</th>
<td>
<input type="password" id="settings_users_new_password" style="width: 95%;" onkeyup="checkPasswordsMatch();" />
</td>
</tr>
<tr>
<th>
Confirm password
</th>
<td>
<input type="password" id="settings_users_new_confirmpassword" style="width: 95%;" onkeyup="checkPasswordsMatch();" />
</td>
</tr>
<tr>
<td colspan="2" id="settings_users_new_label"></td>
</tr>
<tr>
<td colspan="2" style="text-align: right; padding-top: 10px;">
<button value="OK" id="settings_users_new_okbutton" disabled="disabled" onclick="createUser();">OK</button><button value="Cancel" onclick="closeSubDialog();">Cancel</button>
</td>
</tr>
</table>
<script type="text/javascript">
function checkPasswordsMatch() {
var userNameVal = document.getElementById('settings_users_new_username').value;
var newPassword = document.getElementById('settings_users_new_password').value;
var conPassword = document.getElementById('settings_users_new_confirmpassword').value;
var errorLabel = document.getElementById('settings_users_new_label');
var submitButton = document.getElementById('settings_users_new_okbutton');
if (userNameVal.includes("@")) {
if (newPassword == conPassword) {
// check if password meets requirements
if (newPassword.length > 10) {
errorLabel.innerHTML = "";
submitButton.removeAttribute('disabled');
} else {
errorLabel.innerHTML = "Password should be at least 10 characters long";
submitButton.setAttribute('disabled', 'disabled');
}
} else {
errorLabel.innerHTML = "New and confirmed passwords do not match";
submitButton.setAttribute('disabled', 'disabled');
}
} else {
errorLabel.innerHTML = "Email address is invalid";
submitButton.setAttribute('disabled', 'disabled');
}
}
function createUser() {
var model = {
"userName": document.getElementById('settings_users_new_username').value,
"email": document.getElementById('settings_users_new_username').value,
"password": document.getElementById('settings_users_new_password').value,
"confirmPassword": document.getElementById('settings_users_new_confirmpassword').value
}
ajaxCall(
'/api/v1.1/Account/Users',
'POST',
function(result) {
GetUsers();
closeSubDialog();
},
function(error) {
GetUsers();
closeSubDialog();
},
JSON.stringify(model)
);
}
</script>
@@ -28,7 +28,7 @@
document.getElementById('upload_platformoverride').innerHTML = "<option value='0' selected='selected'>Automatic Platform</option>";
var myDropzone = new Dropzone("div#upload_target", {
url: "/api/v1.0/Roms",
url: "/api/v1.1/Roms",
autoProcessQueue: true,
uploadMultiple: true,
paramName: myParamName,
@@ -84,7 +84,7 @@
$('#upload_platformoverride').select2({
minimumInputLength: 3,
ajax: {
url: '/api/v1.0/Search/Platform',
url: '/api/v1.1/Search/Platform',
data: function (params) {
var query = {
SearchString: params.term
@@ -125,6 +125,6 @@
}
console.log(queryString);
myDropzone.options.url = "/api/v1.0/Roms" + queryString;
myDropzone.options.url = "/api/v1.1/Roms" + queryString;
});
</script>
@@ -0,0 +1,122 @@
<div id="properties_toc">
<div id="properties_profile_toc_general" name="properties_profile_toc_item" onclick="ProfileSelectTab('general');">Account</div>
</div>
<div id="properties_bodypanel">
<div id="properties_bodypanel_general" name="properties_profile_tab" style="display: none;">
<h3>Reset Password</h3>
<table style="width: 100%;">
<tr>
<th>Old Password</th>
<td><input type="password" id="profile_oldpassword" style="width: 95%;" /></td>
</tr>
<tr>
<th>New Password</th>
<td><input type="password" id="profile_newpassword" style="width: 95%;" onkeyup="checkPasswordsMatch();" /></td>
</tr>
<tr>
<th>Confirm Password</th>
<td><input type="password" id="profile_confirmpassword" style="width: 95%;" onkeyup="checkPasswordsMatch();" /></td>
</tr>
<tr>
<td colspan="2" id="profile_passwordnotice"></td>
</tr>
<tr>
<td colspan="2" style="text-align: right;">
<button id="profile_resetpassword" value="Reset Password" disabled="disabled" onclick="ResetPassword();">Reset Password</button>
</td>
</tr>
</table>
</div>
</div>
<script type="text/javascript">
document.getElementById('modal-heading').innerHTML = userProfile.emailAddress;
function ProfileSelectTab(TabName) {
var tabs = document.getElementsByName('properties_profile_tab');
for (var i = 0; i < tabs.length; i++) {
if ((tabs[i].id) == ("properties_bodypanel_" + TabName)) {
tabs[i].style.display = '';
} else {
tabs[i].style.display = 'none';
}
}
var tocs = document.getElementsByName('properties_profile_toc_item');
for (var i = 0; i < tocs.length; i++) {
if ((tocs[i].id) == ("properties_profile_toc_" + TabName)) {
tocs[i].className = "properties_toc_item_selected";
} else {
tocs[i].className = '';
}
}
}
function checkPasswordsMatch() {
var oldPassword = document.getElementById('profile_oldpassword').value;
var newPassword = document.getElementById('profile_newpassword').value;
var conPassword = document.getElementById('profile_confirmpassword').value;
var errorLabel = document.getElementById('profile_passwordnotice');
var submitButton = document.getElementById('profile_resetpassword');
// make sure the new password is not the same as the old one
if (newPassword == oldPassword) {
errorLabel.innerHTML = "New password should not match the old password";
submitButton.setAttribute('disabled', 'disabled');
} else {
if (newPassword == conPassword) {
// check if password meets requirements
if (newPassword.length > 10) {
errorLabel.innerHTML = "";
submitButton.removeAttribute('disabled');
} else {
errorLabel.innerHTML = "Password should be at least 10 characters long";
submitButton.setAttribute('disabled', 'disabled');
}
} else {
errorLabel.innerHTML = "New and confirmed passwords do not match";
submitButton.setAttribute('disabled', 'disabled');
}
}
}
function ResetPassword() {
var oldPassword = document.getElementById('profile_oldpassword').value;
var newPassword = document.getElementById('profile_newpassword').value;
var conPassword = document.getElementById('profile_confirmpassword').value;
var model = {
"OldPassword": oldPassword,
"NewPassword": newPassword,
"ConfirmPassword": conPassword
}
ajaxCall(
'/api/v1.1/Account/ChangePassword',
'POST',
function(result) {
ResetPasswordCallback(result);
},
function(error) {
ResetPasswordCallback(error);
},
JSON.stringify(model)
);
}
function ResetPasswordCallback(result) {
var errorLabel = document.getElementById('profile_passwordnotice');
if (result.succeeded == false) {
errorLabel.innerHTML = result.errors.description;
} else {
document.getElementById('profile_oldpassword').value = '';
document.getElementById('profile_newpassword').value = '';
document.getElementById('profile_confirmpassword').value = '';
document.getElementById('profile_resetpassword').setAttribute('disabled', 'disabled');
errorLabel.innerHTML = "Password changed.";
}
}
ProfileSelectTab('general');
</script>
+6 -6
View File
@@ -15,7 +15,7 @@
var emuBios = '';
var emuBackground = '';
ajaxCall('/api/v1.0/Games/' + gameId, 'GET', function (result) {
ajaxCall('/api/v1.1/Games/' + gameId, 'GET', function (result) {
gameData = result;
// load artwork
@@ -27,22 +27,22 @@
} else {
if (result.cover) {
var bg = document.getElementById('bgImage');
bg.setAttribute('style', 'background-image: url("/api/v1.0/Games/' + gameId + '/cover/image"); background-position: center; background-repeat: no-repeat; background-size: cover; filter: blur(10px); -webkit-filter: blur(10px);');
bg.setAttribute('style', 'background-image: url("/api/v1.1/Games/' + gameId + '/cover/image"); background-position: center; background-repeat: no-repeat; background-size: cover; filter: blur(10px); -webkit-filter: blur(10px);');
}
}
if (result.cover) {
emuBackground = '/api/v1.0/Games/' + gameId + '/cover/image';
emuBackground = '/api/v1.1/Games/' + gameId + '/cover/image';
}
emuGameTitle = gameData.name;
});
ajaxCall('/api/v1.0/Bios/' + platformId, 'GET', function (result) {
ajaxCall('/api/v1.1/Bios/' + platformId, 'GET', function (result) {
if (result.length == 0) {
emuBios = '';
} else {
emuBios = '/api/v1.0/Bios/zip/' + platformId;
emuBios = '/api/v1.1/Bios/zip/' + platformId;
}
switch (getQueryString('engine', 'string')) {
@@ -59,7 +59,7 @@
artworksPosition = 0;
}
var bg = document.getElementById('bgImage');
bg.setAttribute('style', 'background-image: url("/api/v1.0/Games/' + gameId + '/artwork/' + artworks[artworksPosition] + '/image"); background-position: center; background-repeat: no-repeat; background-size: cover; filter: blur(10px); -webkit-filter: blur(10px);');
bg.setAttribute('style', 'background-image: url("/api/v1.1/Games/' + gameId + '/artwork/' + artworks[artworksPosition] + '/image"); background-position: center; background-repeat: no-repeat; background-size: cover; filter: blur(10px); -webkit-filter: blur(10px);');
}
}
</script>
+167
View File
@@ -0,0 +1,167 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<script src="/api/v1.1/System/VersionFile"></script>
<link type="text/css" rel="stylesheet" dat-href="/styles/style.css" />
<script src="/scripts/jquery-3.6.0.min.js"></script>
<script src="/scripts/moment.js"></script>
<link href="/styles/select2.min.css" rel="stylesheet" />
<link href="/styles/dropzone.min.css" rel="stylesheet" type="text/css" />
<script src="/scripts/jquery.lazy.min.js"></script>
<script src="/scripts/jquery.lazy.plugins.min.js"></script>
<script src="/scripts/select2.min.js"></script>
<script src="/scripts/dropzone.min.js"></script>
<script src="/scripts/simpleUpload.min.js"></script>
<script src="/scripts/main.js" type="text/javascript"></script>
<script src="/scripts/filterformating.js" type="text/javascript"></script>
<script src="/scripts/gamesformating.js" type="text/javascript"></script>
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
<link rel="manifest" href="/site.webmanifest">
<title>Gaseous Games</title>
<script type="text/javascript">
var head = document.getElementsByTagName('head')[0];
// update links
var headLinks = document.getElementsByTagName('link');
for (var i = 0; i < headLinks.length; i++) {
if (headLinks[i].getAttribute('dat-href') && headLinks[i].rel == "stylesheet") {
var newLink = document.createElement('link');
newLink.rel = "stylesheet";
newLink.href = headLinks[i].getAttribute('dat-href') + '?v=' + AppVersion;
newLink.type = "text/css";
headLinks[i].parentElement.removeChild(headLinks[i]);
head.appendChild(newLink);
}
}
</script>
</head>
<body>
<div id="bgImage" style="background-image: url('/images/LoginWallpaper.jpg'); background-position: center; background-repeat: no-repeat; background-size: cover; filter: blur(10px); -webkit-filter: blur(10px);">
<div id="bgImage_Opacity"></div>
</div>
<div id="content">
<div class="loginwindow" id="first_welcome">
<div id="welcomeform" class="loginwindow-content">
<img src="/images/logo.png" style="display: block; margin: 20px auto;" />
<div id="loginwindow_header_label" style="display: block; text-align: center;">Gaseous Games</div>
<button type="button" value="Get Started" onclick="document.getElementById('first_welcome').style.display = 'none'; document.getElementById('first_newadmin').style.display = '';" style="margin-top: 50px; width: 100%; font-size: 16px; border-radius: 10px; padding-top: 10px; padding-bottom: 10px;">Get Started</button>
</div>
</div>
<div class="loginwindow" id="first_newadmin" style="display: none;">
<div id="loginform" class="loginwindow-content">
<img src="/images/logo.png" style="display: block; margin: 20px auto;" />
<div id="loginwindow_header_label" style="display: block; text-align: center;">Gaseous Games</div>
<table style="width: 100%; margin-top: 20px;" cellpadding="5px">
<tr>
<td colspan="2" style="font-size: 18px;">Create your account.</td>
</tr>
<tr>
<th>Email</th>
<td><input type="email" id="login_email" style="width: 95%;" onkeyup="checkPasswordsMatch();" /></td>
</tr>
<tr>
<th>New Password</th>
<td><input type="password" id="login_password" style="width: 95%;" onkeyup="checkPasswordsMatch();" /></td>
</tr>
<tr>
<th>Confirm Password</th>
<td><input type="password" id="login_confirmpassword" style="width: 95%;" onkeyup="checkPasswordsMatch();" /></td>
</tr>
<tr>
<td colspan="2" id="login_passwordnotice">&nbsp;</td>
</tr>
<tr>
<td colspan="2" style="padding-top: 20px;">
<button id="login_createaccount" type="button" value="Create Account" onclick="registerAccount();" disabled="disabled" style="margin-top: 10px; width: 100%; font-size: 16px; border-radius: 10px; padding-top: 10px; padding-bottom: 10px;">Create Account</button>
</td>
</tr>
</table>
</div>
</div>
</div>
<div id="settings_photocredit">
Wallpaper by <a href="https://unsplash.com/@spideyjoey" class="romlink">Joey Kwok</a> / <a href="https://unsplash.com/photos/a-room-filled-with-arcade-machines-and-neon-lights-jbIsTd7rdd8" class="romlink">Unsplash</a>
</div>
<script type="text/javascript">
// redirect if first run status != 0 as 0 indicates that first run needs to be run
if (FirstRunStatus != 0) {
window.location.replace("/");
}
function checkPasswordsMatch() {
var emailAddress = document.getElementById('login_email').value;
var newPassword = document.getElementById('login_password').value;
var conPassword = document.getElementById('login_confirmpassword').value;
var errorLabel = document.getElementById('login_passwordnotice');
var submitButton = document.getElementById('login_createaccount');
// make sure email address is valid
if (!emailAddress.includes("@")) {
errorLabel.innerHTML = "Please enter a valid email address";
submitButton.setAttribute('disabled', 'disabled');
} else {
if (newPassword == conPassword) {
// check if password meets requirements
if (newPassword.length > 10) {
errorLabel.innerHTML = "&nbsp;";
submitButton.removeAttribute('disabled');
} else {
errorLabel.innerHTML = "Password should be at least 10 characters long";
submitButton.setAttribute('disabled', 'disabled');
}
} else {
errorLabel.innerHTML = "New and confirmed passwords do not match";
submitButton.setAttribute('disabled', 'disabled');
}
}
}
function registerAccount() {
var emailAddress = document.getElementById('login_email').value;
var newPassword = document.getElementById('login_password').value;
var conPassword = document.getElementById('login_confirmpassword').value;
var model = {
"userName": emailAddress,
"email": emailAddress,
"password": newPassword,
"confirmPassword": conPassword
};
ajaxCall(
'/api/v1.1/FirstSetup/0',
'POST',
function(result){
loginCallback(result);
},
function(error){
loginCallback(error);
},
JSON.stringify(model)
);
}
function loginCallback(result) {
switch(result.status) {
case 200:
window.location.replace('/index.html');
break;
default:
// login failed
break;
}
}
</script>
</body>
+20 -20
View File
@@ -94,7 +94,7 @@
var artworksTimer = null;
var selectedScreenshot = 0;
ajaxCall('/api/v1.0/Games/' + gameId, 'GET', function (result) {
ajaxCall('/api/v1.1/Games/' + gameId, 'GET', function (result) {
// populate games page
gameData = result;
@@ -116,7 +116,7 @@
// get alt name
var gameTitleAltLabel = document.getElementById('gametitle_alts');
if (result.alternativeNames) {
ajaxCall('/api/v1.0/Games/' + gameId + '/alternativename', 'GET', function (result) {
ajaxCall('/api/v1.1/Games/' + gameId + '/alternativename', 'GET', function (result) {
var altNames = '';
for (var i = 0; i < result.length; i++) {
if (altNames.length > 0) {
@@ -161,7 +161,7 @@
} else {
var bg = document.getElementById('bgImage');
if (result.cover) {
bg.setAttribute('style', 'background-image: url("/api/v1.0/Games/' + gameId + '/cover/image"); background-position: center; background-repeat: no-repeat; background-size: cover; filter: blur(10px); -webkit-filter: blur(10px);');
bg.setAttribute('style', 'background-image: url("/api/v1.1/Games/' + gameId + '/cover/image"); background-position: center; background-repeat: no-repeat; background-size: cover; filter: blur(10px); -webkit-filter: blur(10px);');
} else {
var randomInt = randomIntFromInterval(1, 3);
bg.setAttribute('style', 'background-image: url("/images/gamebg' + randomInt + '.jpg"); background-position: center; background-repeat: no-repeat; background-size: cover; filter: blur(10px); -webkit-filter: blur(10px);');
@@ -175,7 +175,7 @@
var gameDeveloperLoaded = false;
var gamePublisherLoaded = false;
if (result.involvedCompanies) {
ajaxCall('/api/v1.0/games/' + gameId + '/companies', 'GET', function (result) {
ajaxCall('/api/v1.1/games/' + gameId + '/companies', 'GET', function (result) {
var lstDevelopers = [];
var lstPublishers = [];
@@ -227,7 +227,7 @@
var gameImage = document.createElement('img');
gameImage.className = 'game_cover_image';
if (result.cover) {
gameImage.src = '/api/v1.0/Games/' + result.id + '/cover/image';
gameImage.src = '/api/v1.1/Games/' + result.id + '/cover/image';
} else {
gameImage.src = '/images/unknowngame.png';
gameImage.className = 'game_cover_image unknown';
@@ -240,7 +240,7 @@
var gameRatings = document.createElement('div');
for (var i = 0; i < result.ageRatings.ids.length; i++) {
var ratingImage = document.createElement('img');
ratingImage.src = '/api/v1.0/Games/' + result.id + '/agerating/' + result.ageRatings.ids[i] + '/image';
ratingImage.src = '/api/v1.1/Games/' + result.id + '/agerating/' + result.ageRatings.ids[i] + '/image';
ratingImage.className = 'rating_image';
gameRatings.appendChild(ratingImage);
}
@@ -252,7 +252,7 @@
// load genres
var gameSummaryGenres = document.getElementById('gamesumarry_genres');
if (result.genres) {
ajaxCall('/api/v1.0/Games/' + gameId + '/genre', 'GET', function (result) {
ajaxCall('/api/v1.1/Games/' + gameId + '/genre', 'GET', function (result) {
for (var i = 0; i < result.length; i++) {
var genreLabel = document.createElement('span');
genreLabel.className = 'gamegenrelabel';
@@ -281,7 +281,7 @@
var screenshotItem = document.createElement('div');
screenshotItem.id = 'gamescreenshots_gallery_' + imageIndex;
screenshotItem.setAttribute('name', 'gamescreenshots_gallery_item');
screenshotItem.setAttribute('style', 'background-image: url("/api/v1.0/Games/' + gameId + '/screenshots/' + result.screenshots.ids[i] + '/image"); background-position: center; background-repeat: no-repeat; background-size: contain;)');
screenshotItem.setAttribute('style', 'background-image: url("/api/v1.1/Games/' + gameId + '/screenshots/' + result.screenshots.ids[i] + '/image"); background-position: center; background-repeat: no-repeat; background-size: contain;)');
screenshotItem.setAttribute('imageid', imageIndex);
screenshotItem.setAttribute('imagetype', 0);
screenshotItem.className = 'gamescreenshots_gallery_item';
@@ -293,7 +293,7 @@
// load videos
if (result.videos) {
ajaxCall('/api/v1.0/Games/' + gameId + '/videos', 'GET', function (result) {
ajaxCall('/api/v1.1/Games/' + gameId + '/videos', 'GET', function (result) {
var gameScreenshots_vGallery = document.getElementById('gamescreenshots_gallery_panel');
for (var i = 0; i < result.length; i++) {
var vScreenshotItem = document.createElement('div');
@@ -360,7 +360,7 @@
}
var gameRoms = document.getElementById('gamesummaryroms');
ajaxCall('/api/v1.0/Games/' + gameId + '/roms', 'GET', function (result) {
ajaxCall('/api/v1.1/Games/' + gameId + '/roms', 'GET', function (result) {
if (result.gameRomItems) {
var gameRomItems = result.gameRomItems;
var mediaGroups = result.mediaGroups;
@@ -391,14 +391,14 @@
if (result.gameRomItems[i].emulator) {
if (gameRomItems[i].emulator.type) {
if (gameRomItems[i].emulator.type.length > 0) {
launchButton = '<a href="/index.html?page=emulator&engine=' + gameRomItems[i].emulator.type + '&core=' + gameRomItems[i].emulator.core + '&platformid=' + gameRomItems[i].platform.id + '&gameid=' + gameId + '&rompath=' + encodeURIComponent('/api/v1.0/Games/' + gameId + '/roms/' + gameRomItems[i].id + '/' + encodeURIComponent(gameRomItems[i].name)) + '" class="romstart">Launch</a>';
launchButton = '<a href="/index.html?page=emulator&engine=' + gameRomItems[i].emulator.type + '&core=' + gameRomItems[i].emulator.core + '&platformid=' + gameRomItems[i].platform.id + '&gameid=' + gameId + '&rompath=' + encodeURIComponent('/api/v1.1/Games/' + gameId + '/roms/' + gameRomItems[i].id + '/' + encodeURIComponent(gameRomItems[i].name)) + '" class="romstart">Launch</a>';
}
}
}
var newRow = [
['<input type="checkbox" name="rom_checkbox" data-gameid="' + gameData.id + '" data-platformid="' + gameRomItems[i].platformId + '" data-romid="' + gameRomItems[i].id + '" onclick="handleChecks();" />', 'rom_checkbox_box_hidden', 'rom_edit_checkbox'],
'<a href="/api/v1.0/Games/' + gameId + '/roms/' + gameRomItems[i].id + '/' + encodeURIComponent(gameRomItems[i].name) + '" class="romlink">' + gameRomItems[i].name + '</a>',
'<a href="/api/v1.1/Games/' + gameId + '/roms/' + gameRomItems[i].id + '/' + encodeURIComponent(gameRomItems[i].name) + '" class="romlink">' + gameRomItems[i].name + '</a>',
formatBytes(gameRomItems[i].size, 2),
gameRomItems[i].romTypeMedia,
gameRomItems[i].mediaLabel,
@@ -436,7 +436,7 @@
if (gameRomItem.platformId == mediaGroup.platformId) {
if (gameRomItem.emulator) {
if (gameRomItem.emulator.type.length > 0) {
launchButton = '<a href="/index.html?page=emulator&engine=' + gameRomItem.emulator.type + '&core=' + gameRomItem.emulator.core + '&platformid=' + gameRomItem.platform.id + '&gameid=' + gameId + '&rompath=' + encodeURIComponent('/api/v1.0/Games/' + gameId + '/romgroup/' + mediaGroup.id + '/' + gameData.name + '(' + mediaGroup.id + ')' + '.zip') + '" class="romstart">Launch</a>';
launchButton = '<a href="/index.html?page=emulator&engine=' + gameRomItem.emulator.type + '&core=' + gameRomItem.emulator.core + '&platformid=' + gameRomItem.platform.id + '&gameid=' + gameId + '&rompath=' + encodeURIComponent('/api/v1.1/Games/' + gameId + '/romgroup/' + mediaGroup.id + '/' + gameData.name + '(' + mediaGroup.id + ')' + '.zip') + '" class="romstart">Launch</a>';
break;
}
}
@@ -459,7 +459,7 @@
break;
case "Completed":
statusText = 'Available';
downloadLink = '<a href="/api/v1.0/Games/' + gameId + '/romgroup/' + mediaGroup.id + '/' + gameData.name + '.zip" class="romlink"><img src="/images/download.svg" class="banner_button_image" alt="Download" title="Download" /></a>';
downloadLink = '<a href="/api/v1.1/Games/' + gameId + '/romgroup/' + mediaGroup.id + '/' + gameData.name + '.zip" class="romlink"><img src="/images/download.svg" class="banner_button_image" alt="Download" title="Download" /></a>';
packageSize = formatBytes(mediaGroup.size);
launchButtonContent = launchButton;
break;
@@ -525,7 +525,7 @@
artworksPosition = 0;
}
var bg = document.getElementById('bgImage');
bg.setAttribute('style', 'background-image: url("/api/v1.0/Games/' + gameId + '/artwork/' + artworks[artworksPosition] + '/image"); background-position: center; background-repeat: no-repeat; background-size: cover; filter: blur(10px); -webkit-filter: blur(10px);');
bg.setAttribute('style', 'background-image: url("/api/v1.1/Games/' + gameId + '/artwork/' + artworks[artworksPosition] + '/image"); background-position: center; background-repeat: no-repeat; background-size: cover; filter: blur(10px); -webkit-filter: blur(10px);');
artworksTimer = setTimeout(rotateBackground, 60000);
}
}
@@ -677,7 +677,7 @@
minimumInputLength: 3,
placeholder: "Platform",
ajax: {
url: '/api/v1.0/Search/Platform',
url: '/api/v1.1/Search/Platform',
data: function (params) {
var query = {
SearchString: params.term
@@ -709,7 +709,7 @@
templateResult: DropDownRenderGameOption,
placeholder: "Game",
ajax: {
url: '/api/v1.0/Search/Game',
url: '/api/v1.1/Search/Game',
data: function (params) {
fixplatform = $('#rom_edit_fixplatform').select2('data');
@@ -762,7 +762,7 @@
if (rom_checks[i].checked == true) {
var romId = rom_checks[i].getAttribute('data-romid');
remapCallCounter += 1;
ajaxCall('/api/v1.0/Games/' + gameId + '/roms/' + romId + '?NewPlatformId=' + fixplatform[0].id + '&NewGameId=' + fixgame[0].id, 'PATCH', function (result) {
ajaxCall('/api/v1.1/Games/' + gameId + '/roms/' + romId + '?NewPlatformId=' + fixplatform[0].id + '&NewGameId=' + fixgame[0].id, 'PATCH', function (result) {
remapTitlesCallback();
}, function (result) {
remapTitlesCallback();
@@ -804,7 +804,7 @@
if (rom_checks[i].checked == true) {
var romId = rom_checks[i].getAttribute('data-romid');
remapCallCounter += 1;
ajaxCall('/api/v1.0/Games/' + gameId + '/roms/' + romId, 'DELETE', function (result) {
ajaxCall('/api/v1.1/Games/' + gameId + '/roms/' + romId, 'DELETE', function (result) {
remapTitlesCallback();
});
}
@@ -846,7 +846,7 @@
}
ajaxCall(
'/api/v1.0/Games/' + gameId + '/romgroup?PlatformId=' + platformId,
'/api/v1.1/Games/' + gameId + '/romgroup?PlatformId=' + platformId,
'POST',
function (result) {
DisplayROMCheckboxes(false);
+2 -2
View File
@@ -8,10 +8,10 @@
</div>
<script type="text/javascript">
ajaxCall('/api/v1.0/Filter', 'GET', function (result) {
ajaxCall('/api/v1.1/Filter', 'GET', function (result) {
var filterElement = document.getElementById('games_filter');
formatFilterPanel(filterElement, result);
executeFilter();
executeFilter1_1();
});
</script>
+128
View File
@@ -0,0 +1,128 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<script src="/api/v1.1/System/VersionFile"></script>
<link type="text/css" rel="stylesheet" dat-href="/styles/style.css" />
<script src="/scripts/jquery-3.6.0.min.js"></script>
<script src="/scripts/moment.js"></script>
<link href="/styles/select2.min.css" rel="stylesheet" />
<link href="/styles/dropzone.min.css" rel="stylesheet" type="text/css" />
<script src="/scripts/jquery.lazy.min.js"></script>
<script src="/scripts/jquery.lazy.plugins.min.js"></script>
<script src="/scripts/select2.min.js"></script>
<script src="/scripts/dropzone.min.js"></script>
<script src="/scripts/simpleUpload.min.js"></script>
<script src="/scripts/main.js" type="text/javascript"></script>
<script src="/scripts/filterformating.js" type="text/javascript"></script>
<script src="/scripts/gamesformating.js" type="text/javascript"></script>
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
<link rel="manifest" href="/site.webmanifest">
<title>Gaseous Games</title>
<script type="text/javascript">
var head = document.getElementsByTagName('head')[0];
// update links
var headLinks = document.getElementsByTagName('link');
for (var i = 0; i < headLinks.length; i++) {
if (headLinks[i].getAttribute('dat-href') && headLinks[i].rel == "stylesheet") {
var newLink = document.createElement('link');
newLink.rel = "stylesheet";
newLink.href = headLinks[i].getAttribute('dat-href') + '?v=' + AppVersion;
newLink.type = "text/css";
headLinks[i].parentElement.removeChild(headLinks[i]);
head.appendChild(newLink);
}
}
</script>
</head>
<body>
<div id="bgImage" style="background-image: url('/images/LoginWallpaper.jpg'); background-position: center; background-repeat: no-repeat; background-size: cover; filter: blur(10px); -webkit-filter: blur(10px);">
<div id="bgImage_Opacity"></div>
</div>
<div id="content">
<div class="loginwindow">
<div id="loginform" class="loginwindow-content">
<img src="/images/logo.png" style="display: block; margin: 20px auto;" />
<div id="loginwindow_header_label" style="display: block; text-align: center;">Gaseous Games</div>
<table style="width: 100%; margin-top: 20px;" cellpadding="5px">
<tr>
<th>Email</th>
<td><input type="email" id="login_email" style="width: 95%;" /></td>
</tr>
<tr>
<th>Password</th>
<td><input type="password" id="login_password" style="width: 95%;" /></td>
</tr>
<tr>
<td colspan="2">
<input type="checkbox" id="login_rememberme"> <label for="login_rememberme">Remember Me</label>
</td>
</tr>
<tr>
<td colspan="2" style="padding-top: 20px;">
<button type="button" value="Sign In" onclick="UserLogin();" style="margin-top: 10px; width: 100%; font-size: 16px; border-radius: 10px; padding-top: 10px; padding-bottom: 10px;">Sign In</button>
</td>
</tr>
</table>
</div>
</div>
</div>
<div id="settings_photocredit">
Wallpaper by <a href="https://unsplash.com/@spideyjoey" class="romlink">Joey Kwok</a> / <a href="https://unsplash.com/photos/a-room-filled-with-arcade-machines-and-neon-lights-jbIsTd7rdd8" class="romlink">Unsplash</a>
</div>
<script type="text/javascript">
// redirect if logged in
ajaxCall(
'/api/v1.1/Account/Profile/Basic',
'GET',
function(result) {
// user is signed in - redirect to main page
window.location.replace("/");
},
function(error) {
// user is not signed in - do nothing
}
);
function UserLogin() {
var loginObj = {
"email": document.getElementById('login_email').value,
"password": document.getElementById('login_password').value,
"rememberMe": document.getElementById('login_rememberme').checked
}
ajaxCall(
'/api/v1.1/Account/Login',
'POST',
function(result) {
loginCallback(result);
},
function(error) {
loginCallback(error);
},
JSON.stringify(loginObj)
);
function loginCallback(result) {
switch(result.status) {
case 200:
window.location.replace('/index.html');
break;
default:
// login failed
break;
}
}
}
</script>
</body>
+14 -3
View File
@@ -6,10 +6,11 @@
<div id="properties_toc" class="settings_toc">
<div class="filter_header">Settings</div>
<div id="properties_toc_system" name="properties_toc_item" onclick="SelectTab('system');">System</div>
<div id="properties_toc_settings" name="properties_toc_item" onclick="SelectTab('settings');">Settings</div>
<div id="properties_toc_mapping" name="properties_toc_item" onclick="SelectTab('mapping');">Platform Mapping</div>
<div id="properties_toc_settings" name="properties_toc_item" onclick="SelectTab('settings');" style="display: none;">Settings</div>
<div id="properties_toc_users" name="properties_toc_item" onclick="SelectTab('users');" style="display: none;">Users</div>
<div id="properties_toc_mapping" name="properties_toc_item" onclick="SelectTab('mapping');" style="display: none;">Platform Mapping</div>
<div id="properties_toc_bios" name="properties_toc_item" onclick="SelectTab('bios');">Firmware</div>
<div id="properties_toc_logs" name="properties_toc_item" onclick="SelectTab('logs');">Logs</div>
<div id="properties_toc_logs" name="properties_toc_item" onclick="SelectTab('logs');" style="display: none;">Logs</div>
<div id="properties_toc_about" name="properties_toc_item" onclick="SelectTab('about');">About</div>
</div>
<div id="properties_bodypanel">
@@ -23,6 +24,16 @@
</div>
<script type="text/javascript">
if (userProfile.roles.includes("Admin")) {
document.getElementById('properties_toc_settings').style.display = '';
document.getElementById('properties_toc_users').style.display = '';
document.getElementById('properties_toc_mapping').style.display = '';
document.getElementById('properties_toc_logs').style.display = '';
}
if (userProfile.roles.includes("Gamer")) {
document.getElementById('properties_toc_mapping').style.display = '';
}
var myParam = getQueryString('sub', 'string');
var selectedTab = '';
@@ -8,7 +8,7 @@
</table>
<script type="text/javascript">
ajaxCall('/api/v1.0/Bios', 'GET', function (result) {
ajaxCall('/api/v1.1/Bios', 'GET', function (result) {
result.sort((a, b) => a.platformname.charCodeAt(0) - b.platformname.charCodeAt(0));
var lastPlatform = '';
@@ -28,7 +28,7 @@
}
var biosFilename = document.createElement('a');
biosFilename.href = '/api/v1.0/Bios/' + result[i].platformid + '/' + result[i].filename;
biosFilename.href = '/api/v1.1/Bios/' + result[i].platformid + '/' + result[i].filename;
biosFilename.innerHTML = result[i].filename;
biosFilename.className = 'romlink';
@@ -38,7 +38,7 @@
availableText.className = 'greentext';
biosFilename = document.createElement('a');
biosFilename.href = '/api/v1.0/Bios/' + result[i].platformid + '/' + result[i].filename;
biosFilename.href = '/api/v1.1/Bios/' + result[i].platformid + '/' + result[i].filename;
biosFilename.innerHTML = result[i].filename;
biosFilename.className = 'romlink';
} else {
@@ -26,7 +26,7 @@
}
ajaxCall(
'/api/v1.0/Logs' + apiQuery,
'/api/v1.1/Logs' + apiQuery,
'GET',
function (result) {
var newTable = document.getElementById('settings_events_table');
@@ -6,7 +6,7 @@
<p>This list is pre-populated with some of the more common platforms. New platforms will appear in this list as titles are added.</p>
<p><button value="Export to JSON" onclick="DownloadJSON();">Export to JSON</button><button id="importjson" value="Import JSON">Import JSON</button><button value="Reset to Default" onclick="loadPlatformMapping(true);">Reset to Default</button></p>
<p id="settings_mapping_import" style="display: none;"><button value="Export to JSON" onclick="DownloadJSON();">Export to JSON</button><button id="importjson" value="Import JSON">Import JSON</button><button value="Reset to Default" onclick="loadPlatformMapping(true);">Reset to Default</button></p>
<input id='uploadjson' type='file' name='files' hidden/>
@@ -15,6 +15,10 @@
</table>
<script type="text/javascript">
if (userProfile.roles.includes("Admin")) {
document.getElementById('settings_mapping_import').style.display = '';
}
function loadPlatformMapping(Overwrite) {
var queryString = '';
if (Overwrite == true) {
@@ -22,7 +26,7 @@
}
ajaxCall(
'/api/v1.0/PlatformMaps' + queryString,
'/api/v1.1/PlatformMaps' + queryString,
'GET',
function (result) {
var newTable = document.getElementById('settings_mapping_table');
@@ -47,8 +51,15 @@
hasWebEmulator = 'Yes';
}
var platformLink = '';
if (userProfile.roles.includes("Admin")) {
platformLink = '<a href="#/" onclick="ShowPlatformMappingDialog(' + result[i].igdbId + ');" class="romlink">' + result[i].igdbName + '</a>';
} else {
platformLink = result[i].igdbName;
}
var newRow = [
'<a href="#/" onclick="ShowPlatformMappingDialog(' + result[i].igdbId + ');" class="romlink">' + result[i].igdbName + '</a>',
platformLink,
result[i].extensions.supportedFileExtensions.join(', '),
result[i].extensions.uniqueFileExtensions.join(', '),
hasWebEmulator
@@ -61,7 +72,7 @@
}
function DownloadJSON() {
window.open('/api/v1.0/PlatformMaps', '_blank');
window.open('/api/v1.1/PlatformMaps', '_blank');
}
document.getElementById('importjson').addEventListener('click', openDialog);
@@ -71,7 +82,7 @@
}
$('#uploadjson').change(function () {
$(this).simpleUpload("/api/v1.0/PlatformMaps", {
$(this).simpleUpload("/api/v1.1/PlatformMaps", {
start: function (file) {
//upload started
console.log("JSON upload started");
@@ -11,7 +11,7 @@
<script type="text/javascript">
function drawLibrary() {
ajaxCall(
'/api/v1.0/Library',
'/api/v1.1/Library',
'GET',
function (result) {
var newTable = document.getElementById('settings_libraries');
@@ -26,7 +26,7 @@
<div id="system_signatures"></div>
<script type="text/javascript">function SystemLoadStatus() {
ajaxCall('/api/v1.0/BackgroundTasks', 'GET', function (result) {
ajaxCall('/api/v1.1/BackgroundTasks', 'GET', function (result) {
var newTable = document.createElement('table');
newTable.className = 'romtable';
newTable.setAttribute('cellspacing', 0);
@@ -91,8 +91,10 @@
var itemInterval = result[i].interval;
var nextRunTime = moment(result[i].nextRunTime).fromNow();
var startButton = '';
if (result[i].allowManualStart == true && result[i].itemState != "Running") {
startButton = "<span id='startProcess' class='romstart' onclick='StartProcess(\"" + result[i].itemType + "\");'>Start</span>";
if (userProfile.roles.includes("Admin")) {
if (result[i].allowManualStart == true && result[i].itemState != "Running") {
startButton = "<span id='startProcess' class='romstart' onclick='StartProcess(\"" + result[i].itemType + "\");'>Start</span>";
}
}
if (result[i].allowManualStart == false && result[i].removeWhenStopped == true) {
@@ -119,7 +121,7 @@
}
function SystemLoadSystemStatus() {
ajaxCall('/api/v1.0/System', 'GET', function (result) {
ajaxCall('/api/v1.1/System', 'GET', function (result) {
if (result) {
var totalLibrarySpace = 0;
@@ -234,7 +236,7 @@
}
function SystemSignaturesStatus() {
ajaxCall('/api/v1.0/Signatures/Status', 'GET', function (result) {
ajaxCall('/api/v1.1/Signatures/Status', 'GET', function (result) {
var newTable = document.createElement('table');
newTable.className = 'romtable';
newTable.setAttribute('cellspacing', 0);
@@ -257,7 +259,7 @@
}
function StartProcess(itemType) {
ajaxCall('/api/v1.0/BackgroundTasks/' + itemType + '?ForceRun=true', 'GET', function (result) {
ajaxCall('/api/v1.1/BackgroundTasks/' + itemType + '?ForceRun=true', 'GET', function (result) {
SystemLoadStatus();
});
}
@@ -0,0 +1,99 @@
<div id="gametitle">
<h1 id="gametitle_label">Users</h1>
</div>
<button id="settings_users_new" value="New User" style="float: right;" onclick="showSubDialog('settingsusernew');">New User</button>
<div id="settings_users_table_container">
</div>
<script type="text/javascript">
function GetUsers() {
var targetDiv = document.getElementById('settings_users_table_container');
targetDiv.innerHTML = '';
ajaxCall(
'/api/v1.1/Account/Users',
'GET',
function(result) {
var newTable = document.createElement('table');
newTable.className = 'romtable';
newTable.style.width = '100%';
newTable.cellSpacing = 0;
newTable.appendChild(
createTableRow(
true,
[
'Email',
'Role',
'Age Restriction',
''
],
'',
''
)
);
for (var i = 0; i < result.length; i++) {
var roleDiv = document.createElement('div');
// for (var r = 0; r < result[i].roles.length; r++) {
// var roleItem = document.createElement('div');
// roleItem.className = 'dropdownroleitem';
// roleItem.innerHTML = result[i].roles[r].toUpperCase();
// var colorVal = intToRGB(hashCode(result[i].roles[r]));
// roleItem.style.backgroundColor = '#' + colorVal;
// roleItem.style.borderColor = '#' + colorVal;
// roleDiv.appendChild(roleItem);
// }
var roleItem = CreateBadge(result[i].highestRole);
roleDiv.appendChild(roleItem);
var ageRestrictionPolicyDescription = document.createElement('div');
if (result[i].securityProfile != null) {
if (result[i].securityProfile.ageRestrictionPolicy != null) {
var IncludeUnratedText = '';
if (result[i].securityProfile.ageRestrictionPolicy.includeUnrated == true) {
IncludeUnratedText = " &#43; Unclassified titles";
}
var restrictionText = result[i].securityProfile.ageRestrictionPolicy.maximumAgeRestriction + IncludeUnratedText;
ageRestrictionPolicyDescription = CreateBadge(restrictionText);
}
}
var editButton = '';
var deleteButton = '';
if (userProfile.userId != result[i].id) {
editButton = '<a href="#" onclick="showDialog(\'settingsuseredit\', \'' + result[i].id + '\');" class="romlink"><img src="/images/edit.svg" class="banner_button_image" alt="Edit" title="Edit" /></a>';
deleteButton = '<a href="#" onclick="showSubDialog(\'settingsuserdelete\', \'' + result[i].id + '\');" class="romlink"><img src="/images/delete.svg" class="banner_button_image" alt="Delete" title="Delete" /></a>';
}
newTable.appendChild(
createTableRow(
false,
[
result[i].emailAddress,
roleDiv,
ageRestrictionPolicyDescription,
'<div style="text-align: right;">' + editButton + deleteButton + '</div>'
],
'romrow',
'romcell'
)
);
}
targetDiv.appendChild(newTable);
}
);
}
GetUsers();
</script>
@@ -171,7 +171,7 @@ function buildFilterPanelItem(filterType, itemString, friendlyItemString, tags)
filterPanelItemCheckBoxItem.className = 'filter_panel_item_checkbox';
filterPanelItemCheckBoxItem.name = 'filter_' + filterType;
filterPanelItemCheckBoxItem.setAttribute('filter_id', itemString);
filterPanelItemCheckBoxItem.setAttribute('oninput' , 'executeFilter();');
filterPanelItemCheckBoxItem.setAttribute('oninput' , 'executeFilter1_1();');
if (checkState == true) {
filterPanelItemCheckBoxItem.checked = true;
}
@@ -198,7 +198,7 @@ function executeFilterDelayed() {
filterExecutor = null;
}
filterExecutor = setTimeout(executeFilter, 1000);
filterExecutor = setTimeout(executeFilter1_1, 1000);
}
function executeFilter() {
@@ -299,4 +299,82 @@ function buildFilterTag(tags) {
}
return boundingDiv;
}
function executeFilter1_1() {
console.log("Execute filter 1.1");
var minUserRating = -1;
var minUserRatingInput = document.getElementById('filter_panel_userrating_min');
if (minUserRatingInput.value) {
minUserRating = minUserRatingInput.value;
}
setCookie(minUserRatingInput.id, minUserRatingInput.value);
var maxUserRating = -1;
var maxUserRatingInput = document.getElementById('filter_panel_userrating_max');
if (maxUserRatingInput.value) {
maxUserRating = maxUserRatingInput.value;
}
setCookie(maxUserRatingInput.id, maxUserRatingInput.value);
// build filter model
var model = {
"Name": document.getElementById('filter_panel_search').value,
"Platform": GetFilterQuery1_1('platform'),
"Genre": GetFilterQuery1_1('genre'),
"GameMode": GetFilterQuery1_1('gamemmode'),
"PlayerPerspective": GetFilterQuery1_1('playerperspective'),
"Theme": GetFilterQuery1_1('theme'),
"GameRating": {
"MinimumRating": minUserRating,
"MinimumRatingCount": -1,
"MaximumRating": maxUserRating,
"MaximumRatingCount": -1,
"IncludeUnrated": true
},
"GameAgeRating": {
"AgeGroupings": [
"Child",
"Teen",
"Mature",
"Adult"
],
"IncludeUnrated": true
},
"Sorting": {
"SortBy": "NameThe",
"SortAscenting": true
}
};
console.log('Search model = ' + JSON.stringify(model));
ajaxCall(
'/api/v1.1/Games',
'POST',
function (result) {
var gameElement = document.getElementById('games_library');
formatGamesPanel(gameElement, result);
},
function (error) {
console.log('An error occurred: ' + JSON.stringify(error));
},
JSON.stringify(model)
);
}
function GetFilterQuery1_1(filterName) {
var Filters = document.getElementsByName('filter_' + filterName);
var selections = [];
for (var i = 0; i < Filters.length; i++) {
if (Filters[i].checked) {
setCookie(Filters[i].id, true);
selections.push(Filters[i].getAttribute('filter_id'));
} else {
setCookie(Filters[i].id, false);
}
}
return selections;
}
@@ -20,7 +20,7 @@ function renderGameIcon(gameObject, showTitle, showRatings) {
var gameImage = document.createElement('img');
gameImage.className = 'game_tile_image lazy';
if (gameObject.cover) {
gameImage.setAttribute('data-src', '/api/v1.0/Games/' + gameObject.id + '/cover/image');
gameImage.setAttribute('data-src', '/api/v1.1/Games/' + gameObject.id + '/cover/image');
} else {
gameImage.src = '/images/unknowngame.png';
gameImage.className = 'game_tile_image unknown';
@@ -40,7 +40,7 @@ function renderGameIcon(gameObject, showTitle, showRatings) {
ratingsSection.id = 'ratings_section';
for (var i = 0; i < gameObject.ageRatings.ids.length; i++) {
var ratingImage = document.createElement('img');
ratingImage.src = '/api/v1.0/Games/' + gameObject.id + '/agerating/' + gameObject.ageRatings.ids[i] + '/image';
ratingImage.src = '/api/v1.1/Games/' + gameObject.id + '/agerating/' + gameObject.ageRatings.ids[i] + '/image';
ratingImage.className = 'rating_image_mini';
ratingsSection.appendChild(ratingImage);
}
+15
View File
@@ -373,4 +373,19 @@ function LoadEditableTableData(TableName, Headers, Values) {
eTable.appendChild(row);
}
}
function CreateBadge(BadgeText, ColourOverride) {
var badgeItem = document.createElement('div');
badgeItem.className = 'dropdownroleitem';
badgeItem.innerHTML = BadgeText.toUpperCase();
var colorVal = intToRGB(hashCode(BadgeText));
if (!ColourOverride) {
badgeItem.style.backgroundColor = '#' + colorVal;
badgeItem.style.borderColor = '#' + colorVal;
} else {
badgeItem.style.backgroundColor = ColourOverride;
badgeItem.style.borderColor = ColourOverride;
}
return badgeItem;
}
+103 -7
View File
@@ -30,12 +30,12 @@ h3 {
.modal {
display: none; /* Hidden by default */
position: fixed; /* Stay in place */
z-index: 1; /* Sit on top */
z-index: 100; /* Sit on top */
left: 0;
top: 0;
width: 100%; /* Full width */
height: 100%; /* Full height */
overflow: auto; /* Enable scroll if needed */
overflow: none; /* Enable scroll if needed */
background-color: rgb(0,0,0); /* Fallback color */
background-color: rgba(0,0,0,0.4); /* Black w/ opacity */
backdrop-filter: blur(8px);
@@ -47,9 +47,10 @@ h3 {
/* Modal Content/Box */
.modal-content {
background-color: #383838;
margin: 15% auto; /* 15% from the top and centered */
margin: 10% auto; /* 15% from the top and centered */
padding: 10px;
border: 1px solid #888;
border-radius: 10px;
width: 700px; /* Could be more or less, depending on screen size */
min-height: 358px;
}
@@ -58,6 +59,7 @@ h3 {
margin: 20% auto; /* 20% from the top and centered */
padding: 10px;
border: 1px solid #888;
border-radius: 10px;
width: 300px; /* Could be more or less, depending on screen size */
min-height: 110px;
}
@@ -205,7 +207,7 @@ h3 {
z-index: 1;
}
input[type='text'], input[type='number'] {
input[type='text'], input[type='number'], input[type="email"], input[type="password"] {
background-color: #2b2b2b;
color: white;
padding: 5px;
@@ -409,7 +411,7 @@ input[id='filter_panel_userrating_max'] {
display: inline-block;
max-width: 32px;
max-height: 32px;
margin-right: 2px;
margin-right: 10px;
}
#gamescreenshots {
@@ -621,14 +623,14 @@ th {
height: 100%;
}
div[name="properties_toc_item"] {
div[name="properties_toc_item"],div[name="properties_user_toc_item"],div[name="properties_profile_toc_item"] {
padding: 10px;
border-bottom-width: 1px;
border-bottom-style: solid;
border-bottom-color: #2b2b2b;
}
div[name="properties_toc_item"]:hover {
div[name="properties_toc_item"]:hover,div[name="properties_user_toc_item"]:hover,div[name="properties_profile_toc_item"]:hover {
background-color: #2b2b2b;
cursor: pointer;
}
@@ -965,4 +967,98 @@ button:disabled {
.romGroupTitles {
padding-left: 20px;
}
.loginwindow {
position: fixed; /* Stay in place */
left: 0;
top: 0;
width: 100%; /* Full width */
height: 100%; /* Full height */
overflow: auto; /* Enable scroll if needed */
/*background-color: rgb(0,0,0); /* Fallback color */
/*background-color: rgba(0,0,0,0.4); /* Black w/ opacity */
/*backdrop-filter: blur(8px);
-webkit-backdrop-filter: blur(8px);*/
filter: drop-shadow(5px 5px 10px #000);
-webkit-filter: drop-shadow(5px 5px 10px #000);
}
/* Modal Content/Box */
.loginwindow-content {
position: relative;
background-color: #383838;
margin: 15% auto; /* 15% from the top and centered */
padding: 10px;
border: 1px solid #888;
border-radius: 10px;
width: 350px; /* Could be more or less, depending on screen size */
min-height: 250px;
}
#loginwindow_header_label {
font-family: Commodore64;
display: inline-block;
padding: 10px;
font-size: 24pt;
vertical-align: top;
/*color: #edeffa;*/
color: #7c70da;
}
/* The container <div> - needed to position the dropdown content */
.dropdown {
position: relative;
display: inline-block;
float: right;
}
/* Dropdown Content (Hidden by Default) */
.dropdown-content {
display: none;
position: absolute;
background-color: #f1f1f1;
min-width: 160px;
/* box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2); */
z-index: 1;
right: 0;
top: 40px;
filter: drop-shadow(5px 5px 10px #000);
-webkit-filter: drop-shadow(5px 5px 10px #000);
}
/* Links inside the dropdown */
.dropdown-content a, .dropdown-content span {
color: black;
padding: 12px 16px;
text-decoration: none;
display: block;
}
.dropdown-content span {
cursor: auto;
}
/* Change color of dropdown links on hover */
.dropdown-content a:hover {background-color: #ddd;}
/* Show the dropdown menu (use JS to add this class to the .dropdown-content container when the user clicks on the dropdown button) */
.show {display:block;}
.dropdownroleitem {
text-transform: capitalize;
font-size: 12px;
font-weight: bold;
color: white;
background-color: red;
border-color: red;
border-style: solid;
border-width: 1px;
border-radius: 5px;
padding: 3px 6px;
display: inline-block;
margin-top: 5px;
margin-bottom: 5px;
margin-left: 5px;
margin-right: 5px;
}