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

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -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
{
}
}

View File

@@ -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; }
}
}

View File

@@ -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);
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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; }
}
}

View File

@@ -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; }
}

View File

@@ -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; }
}

View File

@@ -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; }
}

View File

@@ -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; }
}

View File

@@ -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; }
}

View File

@@ -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;
}
}
}
}

View File

@@ -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; }
}

View File

@@ -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; }
}
}
}

View File

@@ -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; }
}

View File

@@ -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; }
}

View File

@@ -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; }
}

View File

@@ -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;
}
}
}
}

View File

@@ -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; }
}

View File

@@ -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; }
}

View File

@@ -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; }
}

View File

@@ -147,6 +147,50 @@ namespace gaseous_server.Classes
return _ExecuteCMD(Command, Parameters, Timeout, ConnectionString); 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 = "") private DataTable _ExecuteCMD(string Command, Dictionary<string, object> Parameters, int Timeout = 30, string ConnectionString = "")
{ {
if (ConnectionString == "") { ConnectionString = _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) public void ExecuteTransactionCMD(List<SQLTransactionItem> CommandList, int Timeout = 60)
{ {
object conn; object conn;
@@ -284,6 +357,47 @@ namespace gaseous_server.Classes
return RetTable; 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) public void TransactionExecCMD(List<Dictionary<string, object>> Parameters, int Timeout)
{ {
var conn = new MySqlConnection(DBConn); var conn = new MySqlConnection(DBConn);

View File

@@ -1,5 +1,6 @@
using System; using System;
using System.Reflection; using System.Reflection;
using System.Text.Json.Serialization;
using IGDB; using IGDB;
using IGDB.Models; using IGDB.Models;
@@ -151,6 +152,202 @@ namespace gaseous_server.Classes.Metadata
public AgeRatingTitle RatingTitle { get; set; } public AgeRatingTitle RatingTitle { get; set; }
public string[] Descriptions { 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; }
}
}
} }
} }

View File

@@ -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();
}
}
}
}

View File

@@ -2,6 +2,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
namespace gaseous_server.Controllers namespace gaseous_server.Controllers
@@ -9,9 +10,12 @@ namespace gaseous_server.Controllers
[ApiController] [ApiController]
[Route("api/v{version:apiVersion}/[controller]")] [Route("api/v{version:apiVersion}/[controller]")]
[ApiVersion("1.0")] [ApiVersion("1.0")]
[ApiVersion("1.1")]
[Authorize(Roles = "Admin,Gamer,Player")]
public class BackgroundTasksController : Controller public class BackgroundTasksController : Controller
{ {
[MapToApiVersion("1.0")] [MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status200OK)]
public List<ProcessQueue.QueueItem> GetQueue() public List<ProcessQueue.QueueItem> GetQueue()
@@ -20,10 +24,12 @@ namespace gaseous_server.Controllers
} }
[MapToApiVersion("1.0")] [MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[Route("{TaskType}")] [Route("{TaskType}")]
[ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status404NotFound)]
[Authorize(Roles = "Admin")]
public ActionResult<ProcessQueue.QueueItem> ForceRun(ProcessQueue.QueueItemType TaskType, Boolean ForceRun) public ActionResult<ProcessQueue.QueueItem> ForceRun(ProcessQueue.QueueItemType TaskType, Boolean ForceRun)
{ {
foreach (ProcessQueue.QueueItem qi in ProcessQueue.QueueItems) foreach (ProcessQueue.QueueItem qi in ProcessQueue.QueueItems)

View File

@@ -4,6 +4,7 @@ using System.IO.Compression;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using gaseous_server.Classes; using gaseous_server.Classes;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
namespace gaseous_server.Controllers namespace gaseous_server.Controllers
@@ -11,9 +12,12 @@ namespace gaseous_server.Controllers
[ApiController] [ApiController]
[Route("api/v{version:apiVersion}/[controller]")] [Route("api/v{version:apiVersion}/[controller]")]
[ApiVersion("1.0")] [ApiVersion("1.0")]
[ApiVersion("1.1")]
[Authorize]
public class BiosController : Controller public class BiosController : Controller
{ {
[MapToApiVersion("1.0")] [MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status200OK)]
public List<Classes.Bios.BiosItem> GetBios() public List<Classes.Bios.BiosItem> GetBios()
@@ -22,6 +26,7 @@ namespace gaseous_server.Controllers
} }
[MapToApiVersion("1.0")] [MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[Route("{PlatformId}")] [Route("{PlatformId}")]
[ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status200OK)]
@@ -31,8 +36,10 @@ namespace gaseous_server.Controllers
} }
[MapToApiVersion("1.0")] [MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[MapToApiVersion("1.0")] [MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpHead] [HttpHead]
[Route("zip/{PlatformId}")] [Route("zip/{PlatformId}")]
[ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)] [ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)]
@@ -66,8 +73,10 @@ namespace gaseous_server.Controllers
} }
[MapToApiVersion("1.0")] [MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[MapToApiVersion("1.0")] [MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpHead] [HttpHead]
[Route("{PlatformId}/{BiosName}")] [Route("{PlatformId}/{BiosName}")]
[ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)] [ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)]

View File

@@ -4,6 +4,7 @@ using System.IO.Compression;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using gaseous_server.Classes; using gaseous_server.Classes;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
namespace gaseous_server.Controllers namespace gaseous_server.Controllers
@@ -11,6 +12,8 @@ namespace gaseous_server.Controllers
[ApiController] [ApiController]
[Route("api/v{version:apiVersion}/[controller]")] [Route("api/v{version:apiVersion}/[controller]")]
[ApiVersion("1.0")] [ApiVersion("1.0")]
[ApiVersion("1.1")]
[Authorize]
public class CollectionsController : Controller public class CollectionsController : Controller
{ {
/// <summary> /// <summary>
@@ -18,6 +21,7 @@ namespace gaseous_server.Controllers
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
[MapToApiVersion("1.0")] [MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status200OK)]
public List<Classes.Collections.CollectionItem> GetCollections() 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> /// <param name="Build">Set to true to begin the collection build process</param>
/// <returns></returns> /// <returns></returns>
[MapToApiVersion("1.0")] [MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[Route("{CollectionId}")] [Route("{CollectionId}")]
[ProducesResponseType(typeof(Classes.Collections.CollectionItem), StatusCodes.Status200OK)] [ProducesResponseType(typeof(Classes.Collections.CollectionItem), StatusCodes.Status200OK)]
@@ -59,6 +64,7 @@ namespace gaseous_server.Controllers
/// <param name="CollectionId"></param> /// <param name="CollectionId"></param>
/// <returns></returns> /// <returns></returns>
[MapToApiVersion("1.0")] [MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[Route("{CollectionId}/Roms")] [Route("{CollectionId}/Roms")]
[ProducesResponseType(typeof(List<Classes.Collections.CollectionContents.CollectionPlatformItem>), StatusCodes.Status200OK)] [ProducesResponseType(typeof(List<Classes.Collections.CollectionContents.CollectionPlatformItem>), StatusCodes.Status200OK)]
@@ -82,8 +88,10 @@ namespace gaseous_server.Controllers
/// <param name="Item"></param> /// <param name="Item"></param>
/// <returns></returns> /// <returns></returns>
[MapToApiVersion("1.0")] [MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpPost] [HttpPost]
[Route("Preview")] [Route("Preview")]
[Authorize(Roles = "Admin,Gamer")]
[ProducesResponseType(typeof(List<Classes.Collections.CollectionContents.CollectionPlatformItem>), StatusCodes.Status200OK)] [ProducesResponseType(typeof(List<Classes.Collections.CollectionContents.CollectionPlatformItem>), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status404NotFound)]
public ActionResult GetCollectionRomsPreview(Classes.Collections.CollectionItem Item) public ActionResult GetCollectionRomsPreview(Classes.Collections.CollectionItem Item)
@@ -104,6 +112,7 @@ namespace gaseous_server.Controllers
/// <param name="CollectionId"></param> /// <param name="CollectionId"></param>
/// <returns></returns> /// <returns></returns>
[MapToApiVersion("1.0")] [MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[Route("{CollectionId}/Roms/Zip")] [Route("{CollectionId}/Roms/Zip")]
[ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)] [ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)]
@@ -138,7 +147,9 @@ namespace gaseous_server.Controllers
/// <param name="Item"></param> /// <param name="Item"></param>
/// <returns></returns> /// <returns></returns>
[MapToApiVersion("1.0")] [MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpPost] [HttpPost]
[Authorize(Roles = "Admin,Gamer")]
[ProducesResponseType(typeof(Classes.Collections.CollectionItem), StatusCodes.Status200OK)] [ProducesResponseType(typeof(Classes.Collections.CollectionItem), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status400BadRequest)]
public ActionResult NewCollection(Classes.Collections.CollectionItem Item) public ActionResult NewCollection(Classes.Collections.CollectionItem Item)
@@ -160,8 +171,10 @@ namespace gaseous_server.Controllers
/// <param name="Item"></param> /// <param name="Item"></param>
/// <returns></returns> /// <returns></returns>
[MapToApiVersion("1.0")] [MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpPatch] [HttpPatch]
[Route("{CollectionId}")] [Route("{CollectionId}")]
[Authorize(Roles = "Admin,Gamer")]
[ProducesResponseType(typeof(Classes.Collections.CollectionItem), StatusCodes.Status200OK)] [ProducesResponseType(typeof(Classes.Collections.CollectionItem), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status404NotFound)]
public ActionResult EditCollection(long CollectionId, Classes.Collections.CollectionItem Item) public ActionResult EditCollection(long CollectionId, Classes.Collections.CollectionItem Item)
@@ -183,7 +196,9 @@ namespace gaseous_server.Controllers
/// <param name="Item"></param> /// <param name="Item"></param>
/// <returns></returns> /// <returns></returns>
[MapToApiVersion("1.0")] [MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpPatch] [HttpPatch]
[Authorize(Roles = "Admin,Gamer")]
[Route("{CollectionId}/AlwaysInclude")] [Route("{CollectionId}/AlwaysInclude")]
[ProducesResponseType(typeof(Classes.Collections.CollectionItem), StatusCodes.Status200OK)] [ProducesResponseType(typeof(Classes.Collections.CollectionItem), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status404NotFound)]
@@ -218,7 +233,9 @@ namespace gaseous_server.Controllers
/// </summary> /// </summary>
/// <param name="CollectionId"></param> /// <param name="CollectionId"></param>
[MapToApiVersion("1.0")] [MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpDelete] [HttpDelete]
[Authorize(Roles = "Admin,Gamer")]
[Route("{CollectionId}")] [Route("{CollectionId}")]
[ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status404NotFound)]

View File

@@ -5,6 +5,7 @@ using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using gaseous_server.Classes; using gaseous_server.Classes;
using IGDB.Models; using IGDB.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
@@ -12,10 +13,13 @@ namespace gaseous_server.Controllers
{ {
[Route("api/v{version:apiVersion}/[controller]")] [Route("api/v{version:apiVersion}/[controller]")]
[ApiVersion("1.0")] [ApiVersion("1.0")]
[ApiVersion("1.1")]
[Authorize]
[ApiController] [ApiController]
public class FilterController : ControllerBase public class FilterController : ControllerBase
{ {
[MapToApiVersion("1.0")] [MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status200OK)]
//[ResponseCache(CacheProfileName = "5Minute")] //[ResponseCache(CacheProfileName = "5Minute")]

View File

@@ -9,6 +9,7 @@ using System.Threading.Tasks;
using gaseous_server.Classes; using gaseous_server.Classes;
using gaseous_server.Classes.Metadata; using gaseous_server.Classes.Metadata;
using IGDB.Models; using IGDB.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.CodeAnalysis.Scripting; using Microsoft.CodeAnalysis.Scripting;
@@ -18,6 +19,8 @@ namespace gaseous_server.Controllers
{ {
[Route("api/v{version:apiVersion}/[controller]")] [Route("api/v{version:apiVersion}/[controller]")]
[ApiVersion("1.0")] [ApiVersion("1.0")]
[ApiVersion("1.1")]
[Authorize]
[ApiController] [ApiController]
public class GamesController : ControllerBase public class GamesController : ControllerBase
{ {
@@ -36,7 +39,7 @@ namespace gaseous_server.Controllers
bool sortdescending = false) 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( public static List<Game> GetGames(
@@ -48,6 +51,9 @@ namespace gaseous_server.Controllers
string theme = "", string theme = "",
int minrating = -1, int minrating = -1,
int maxrating = -1, int maxrating = -1,
string ratinggroup = "Adult",
bool includenullrating = true,
bool sortbynamethe = false,
bool sortdescending = false) bool sortdescending = false)
{ {
string whereClause = ""; string whereClause = "";
@@ -170,6 +176,52 @@ namespace gaseous_server.Controllers
whereClauses.Add(tempVal); 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 // build where clause
if (whereClauses.Count > 0) if (whereClauses.Count > 0)
{ {
@@ -199,14 +251,21 @@ namespace gaseous_server.Controllers
} }
// order by clause // 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) if (sortdescending == true)
{ {
orderByClause = "ORDER BY `Name` DESC"; orderByClause = "ORDER BY `" + orderByField + "` DESC";
} }
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); 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>(); List<IGDB.Models.Game> RetVal = new List<IGDB.Models.Game>();
@@ -221,6 +280,7 @@ namespace gaseous_server.Controllers
} }
[MapToApiVersion("1.0")] [MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[Route("{GameId}")] [Route("{GameId}")]
[ProducesResponseType(typeof(Game), StatusCodes.Status200OK)] [ProducesResponseType(typeof(Game), StatusCodes.Status200OK)]
@@ -248,6 +308,7 @@ namespace gaseous_server.Controllers
} }
[MapToApiVersion("1.0")] [MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[Route("{GameId}/alternativename")] [Route("{GameId}/alternativename")]
[ProducesResponseType(typeof(List<AlternativeName>), StatusCodes.Status200OK)] [ProducesResponseType(typeof(List<AlternativeName>), StatusCodes.Status200OK)]
@@ -280,6 +341,7 @@ namespace gaseous_server.Controllers
} }
[MapToApiVersion("1.0")] [MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[Route("{GameId}/agerating")] [Route("{GameId}/agerating")]
[ProducesResponseType(typeof(List<AgeRatings.GameAgeRating>), StatusCodes.Status200OK)] [ProducesResponseType(typeof(List<AgeRatings.GameAgeRating>), StatusCodes.Status200OK)]
@@ -312,6 +374,7 @@ namespace gaseous_server.Controllers
} }
[MapToApiVersion("1.0")] [MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[Route("{GameId}/agerating/{RatingId}/image")] [Route("{GameId}/agerating/{RatingId}/image")]
[ProducesResponseType(typeof(FileContentResult), StatusCodes.Status200OK)] [ProducesResponseType(typeof(FileContentResult), StatusCodes.Status200OK)]
@@ -392,6 +455,7 @@ namespace gaseous_server.Controllers
} }
[MapToApiVersion("1.0")] [MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[Route("{GameId}/artwork")] [Route("{GameId}/artwork")]
[ProducesResponseType(typeof(List<Artwork>), StatusCodes.Status200OK)] [ProducesResponseType(typeof(List<Artwork>), StatusCodes.Status200OK)]
@@ -422,6 +486,7 @@ namespace gaseous_server.Controllers
} }
[MapToApiVersion("1.0")] [MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[Route("{GameId}/artwork/{ArtworkId}")] [Route("{GameId}/artwork/{ArtworkId}")]
[ProducesResponseType(typeof(Artwork), StatusCodes.Status200OK)] [ProducesResponseType(typeof(Artwork), StatusCodes.Status200OK)]
@@ -457,6 +522,7 @@ namespace gaseous_server.Controllers
} }
[MapToApiVersion("1.0")] [MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[Route("{GameId}/artwork/{ArtworkId}/image")] [Route("{GameId}/artwork/{ArtworkId}/image")]
[ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)] [ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)]
@@ -512,6 +578,7 @@ namespace gaseous_server.Controllers
} }
[MapToApiVersion("1.0")] [MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[Route("{GameId}/cover")] [Route("{GameId}/cover")]
[ProducesResponseType(typeof(Cover), StatusCodes.Status200OK)] [ProducesResponseType(typeof(Cover), StatusCodes.Status200OK)]
@@ -546,6 +613,7 @@ namespace gaseous_server.Controllers
} }
[MapToApiVersion("1.0")] [MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[Route("{GameId}/cover/image")] [Route("{GameId}/cover/image")]
[ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)] [ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)]
@@ -586,6 +654,7 @@ namespace gaseous_server.Controllers
} }
[MapToApiVersion("1.0")] [MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[Route("{GameId}/genre")] [Route("{GameId}/genre")]
[ProducesResponseType(typeof(List<Genre>), StatusCodes.Status200OK)] [ProducesResponseType(typeof(List<Genre>), StatusCodes.Status200OK)]
@@ -623,6 +692,7 @@ namespace gaseous_server.Controllers
} }
[MapToApiVersion("1.0")] [MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[Route("{GameId}/companies")] [Route("{GameId}/companies")]
[ProducesResponseType(typeof(List<Dictionary<string, object>>), StatusCodes.Status200OK)] [ProducesResponseType(typeof(List<Dictionary<string, object>>), StatusCodes.Status200OK)]
@@ -667,6 +737,7 @@ namespace gaseous_server.Controllers
} }
[MapToApiVersion("1.0")] [MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[Route("{GameId}/companies/{CompanyId}")] [Route("{GameId}/companies/{CompanyId}")]
[ProducesResponseType(typeof(Dictionary<string, object>), StatusCodes.Status200OK)] [ProducesResponseType(typeof(Dictionary<string, object>), StatusCodes.Status200OK)]
@@ -709,6 +780,7 @@ namespace gaseous_server.Controllers
} }
[MapToApiVersion("1.0")] [MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[Route("{GameId}/companies/{CompanyId}/image")] [Route("{GameId}/companies/{CompanyId}/image")]
[ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)] [ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)]
@@ -753,6 +825,7 @@ namespace gaseous_server.Controllers
} }
[MapToApiVersion("1.0")] [MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[Route("{GameId}/roms")] [Route("{GameId}/roms")]
[ProducesResponseType(typeof(Classes.Roms.GameRomObject), StatusCodes.Status200OK)] [ProducesResponseType(typeof(Classes.Roms.GameRomObject), StatusCodes.Status200OK)]
@@ -773,6 +846,7 @@ namespace gaseous_server.Controllers
} }
[MapToApiVersion("1.0")] [MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[Route("{GameId}/roms/{RomId}")] [Route("{GameId}/roms/{RomId}")]
[ProducesResponseType(typeof(Classes.Roms.GameRomItem), StatusCodes.Status200OK)] [ProducesResponseType(typeof(Classes.Roms.GameRomItem), StatusCodes.Status200OK)]
@@ -801,7 +875,9 @@ namespace gaseous_server.Controllers
} }
[MapToApiVersion("1.0")] [MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpPatch] [HttpPatch]
[Authorize(Roles = "Admin,Gamer")]
[Route("{GameId}/roms/{RomId}")] [Route("{GameId}/roms/{RomId}")]
[ProducesResponseType(typeof(Classes.Roms.GameRomItem), StatusCodes.Status200OK)] [ProducesResponseType(typeof(Classes.Roms.GameRomItem), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status404NotFound)]
@@ -829,7 +905,9 @@ namespace gaseous_server.Controllers
} }
[MapToApiVersion("1.0")] [MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpDelete] [HttpDelete]
[Authorize(Roles = "Admin,Gamer")]
[Route("{GameId}/roms/{RomId}")] [Route("{GameId}/roms/{RomId}")]
[ProducesResponseType(typeof(Classes.Roms.GameRomItem), StatusCodes.Status200OK)] [ProducesResponseType(typeof(Classes.Roms.GameRomItem), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status404NotFound)]
@@ -857,8 +935,10 @@ namespace gaseous_server.Controllers
} }
[MapToApiVersion("1.0")] [MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[MapToApiVersion("1.0")] [MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpHead] [HttpHead]
[Route("{GameId}/roms/{RomId}/file")] [Route("{GameId}/roms/{RomId}/file")]
[ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)] [ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)]
@@ -894,8 +974,10 @@ namespace gaseous_server.Controllers
} }
[MapToApiVersion("1.0")] [MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[MapToApiVersion("1.0")] [MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpHead] [HttpHead]
[Route("{GameId}/roms/{RomId}/{FileName}")] [Route("{GameId}/roms/{RomId}/{FileName}")]
[ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)] [ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)]
@@ -931,6 +1013,7 @@ namespace gaseous_server.Controllers
} }
[MapToApiVersion("1.0")] [MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[Route("{GameId}/romgroup/{RomGroupId}")] [Route("{GameId}/romgroup/{RomGroupId}")]
[ProducesResponseType(typeof(Classes.RomMediaGroup.GameRomMediaGroupItem), StatusCodes.Status200OK)] [ProducesResponseType(typeof(Classes.RomMediaGroup.GameRomMediaGroupItem), StatusCodes.Status200OK)]
@@ -959,7 +1042,9 @@ namespace gaseous_server.Controllers
} }
[MapToApiVersion("1.0")] [MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpPost] [HttpPost]
[Authorize(Roles = "Admin,Gamer")]
[Route("{GameId}/romgroup")] [Route("{GameId}/romgroup")]
[ProducesResponseType(typeof(Classes.RomMediaGroup.GameRomMediaGroupItem), StatusCodes.Status200OK)] [ProducesResponseType(typeof(Classes.RomMediaGroup.GameRomMediaGroupItem), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status404NotFound)]
@@ -986,7 +1071,9 @@ namespace gaseous_server.Controllers
} }
[MapToApiVersion("1.0")] [MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpPatch] [HttpPatch]
[Authorize(Roles = "Admin,Gamer")]
[Route("{GameId}/romgroup/{RomId}")] [Route("{GameId}/romgroup/{RomId}")]
[ProducesResponseType(typeof(Classes.RomMediaGroup.GameRomMediaGroupItem), StatusCodes.Status200OK)] [ProducesResponseType(typeof(Classes.RomMediaGroup.GameRomMediaGroupItem), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status404NotFound)]
@@ -1014,7 +1101,9 @@ namespace gaseous_server.Controllers
} }
[MapToApiVersion("1.0")] [MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpDelete] [HttpDelete]
[Authorize(Roles = "Admin,Gamer")]
[Route("{GameId}/romgroup/{RomGroupId}")] [Route("{GameId}/romgroup/{RomGroupId}")]
[ProducesResponseType(typeof(Classes.RomMediaGroup.GameRomMediaGroupItem), StatusCodes.Status200OK)] [ProducesResponseType(typeof(Classes.RomMediaGroup.GameRomMediaGroupItem), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status404NotFound)]
@@ -1042,8 +1131,10 @@ namespace gaseous_server.Controllers
} }
[MapToApiVersion("1.0")] [MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[MapToApiVersion("1.0")] [MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpHead] [HttpHead]
[Route("{GameId}/romgroup/{RomGroupId}/file")] [Route("{GameId}/romgroup/{RomGroupId}/file")]
[Route("{GameId}/romgroup/{RomGroupId}/{filename}")] [Route("{GameId}/romgroup/{RomGroupId}/{filename}")]
@@ -1089,6 +1180,7 @@ namespace gaseous_server.Controllers
} }
[MapToApiVersion("1.0")] [MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[Route("search")] [Route("search")]
[ProducesResponseType(typeof(List<Game>), StatusCodes.Status200OK)] [ProducesResponseType(typeof(List<Game>), StatusCodes.Status200OK)]
@@ -1127,6 +1219,7 @@ namespace gaseous_server.Controllers
} }
[MapToApiVersion("1.0")] [MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[Route("{GameId}/screenshots")] [Route("{GameId}/screenshots")]
[ProducesResponseType(typeof(List<Screenshot>), StatusCodes.Status200OK)] [ProducesResponseType(typeof(List<Screenshot>), StatusCodes.Status200OK)]
@@ -1157,6 +1250,7 @@ namespace gaseous_server.Controllers
} }
[MapToApiVersion("1.0")] [MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[Route("{GameId}/screenshots/{ScreenshotId}")] [Route("{GameId}/screenshots/{ScreenshotId}")]
[ProducesResponseType(typeof(Screenshot), StatusCodes.Status200OK)] [ProducesResponseType(typeof(Screenshot), StatusCodes.Status200OK)]
@@ -1190,6 +1284,7 @@ namespace gaseous_server.Controllers
} }
[MapToApiVersion("1.0")] [MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[Route("{GameId}/screenshots/{ScreenshotId}/image")] [Route("{GameId}/screenshots/{ScreenshotId}/image")]
[ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)] [ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)]
@@ -1233,6 +1328,7 @@ namespace gaseous_server.Controllers
} }
[MapToApiVersion("1.0")] [MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[Route("{GameId}/videos")] [Route("{GameId}/videos")]
[ProducesResponseType(typeof(List<GameVideo>), StatusCodes.Status200OK)] [ProducesResponseType(typeof(List<GameVideo>), StatusCodes.Status200OK)]

View File

@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.IO.Compression; using System.IO.Compression;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
namespace gaseous_server.Controllers namespace gaseous_server.Controllers
@@ -10,9 +11,12 @@ namespace gaseous_server.Controllers
[ApiController] [ApiController]
[Route("api/v{version:apiVersion}/[controller]")] [Route("api/v{version:apiVersion}/[controller]")]
[ApiVersion("1.0")] [ApiVersion("1.0")]
[ApiVersion("1.1")]
[Authorize(Roles = "Admin")]
public class LibraryController : Controller public class LibraryController : Controller
{ {
[MapToApiVersion("1.0")] [MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[ProducesResponseType(typeof(List<GameLibrary.LibraryItem>), StatusCodes.Status200OK)] [ProducesResponseType(typeof(List<GameLibrary.LibraryItem>), StatusCodes.Status200OK)]
public ActionResult GetLibraries() public ActionResult GetLibraries()
@@ -21,6 +25,7 @@ namespace gaseous_server.Controllers
} }
[MapToApiVersion("1.0")] [MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet("{LibraryId}")] [HttpGet("{LibraryId}")]
[ProducesResponseType(typeof(GameLibrary.LibraryItem), StatusCodes.Status200OK)] [ProducesResponseType(typeof(GameLibrary.LibraryItem), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status404NotFound)]
@@ -37,6 +42,7 @@ namespace gaseous_server.Controllers
} }
[MapToApiVersion("1.0")] [MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpPost] [HttpPost]
[ProducesResponseType(typeof(GameLibrary.LibraryItem), StatusCodes.Status200OK)] [ProducesResponseType(typeof(GameLibrary.LibraryItem), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status404NotFound)]
@@ -58,6 +64,7 @@ namespace gaseous_server.Controllers
} }
[MapToApiVersion("1.0")] [MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpDelete("{LibraryId}")] [HttpDelete("{LibraryId}")]
[ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status400BadRequest)]

View File

@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using gaseous_server.Classes; using gaseous_server.Classes;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
namespace gaseous_server.Controllers namespace gaseous_server.Controllers
@@ -10,9 +11,12 @@ namespace gaseous_server.Controllers
[ApiController] [ApiController]
[Route("api/v{version:apiVersion}/[controller]")] [Route("api/v{version:apiVersion}/[controller]")]
[ApiVersion("1.0")] [ApiVersion("1.0")]
[ApiVersion("1.1")]
[Authorize(Roles = "Admin")]
public class LogsController : Controller public class LogsController : Controller
{ {
[MapToApiVersion("1.0")] [MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status200OK)]
public List<Logging.LogItem> Logs(long? StartIndex, int PageNumber = 1, int PageSize = 100) public List<Logging.LogItem> Logs(long? StartIndex, int PageNumber = 1, int PageSize = 100)

View File

@@ -12,15 +12,19 @@ using IGDB.Models;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.CodeAnalysis.Scripting; using Microsoft.CodeAnalysis.Scripting;
using Microsoft.AspNetCore.Authorization;
namespace gaseous_server.Controllers namespace gaseous_server.Controllers
{ {
[Route("api/v{version:apiVersion}/[controller]")] [Route("api/v{version:apiVersion}/[controller]")]
[ApiVersion("1.0")] [ApiVersion("1.0")]
[ApiVersion("1.1")]
[ApiController] [ApiController]
[Authorize]
public class PlatformMapsController : Controller public class PlatformMapsController : Controller
{ {
[MapToApiVersion("1.0")] [MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[ProducesResponseType(typeof(List<PlatformMapping.PlatformMapItem>), StatusCodes.Status200OK)] [ProducesResponseType(typeof(List<PlatformMapping.PlatformMapItem>), StatusCodes.Status200OK)]
public ActionResult GetPlatformMap(bool ResetToDefault = false) public ActionResult GetPlatformMap(bool ResetToDefault = false)
@@ -34,6 +38,7 @@ namespace gaseous_server.Controllers
} }
[MapToApiVersion("1.0")] [MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[Route("{PlatformId}")] [Route("{PlatformId}")]
[ProducesResponseType(typeof(PlatformMapping.PlatformMapItem), StatusCodes.Status200OK)] [ProducesResponseType(typeof(PlatformMapping.PlatformMapItem), StatusCodes.Status200OK)]
@@ -60,10 +65,12 @@ namespace gaseous_server.Controllers
} }
[MapToApiVersion("1.0")] [MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpPost] [HttpPost]
[ProducesResponseType(typeof(List<IFormFile>), StatusCodes.Status200OK)] [ProducesResponseType(typeof(List<IFormFile>), StatusCodes.Status200OK)]
[RequestSizeLimit(long.MaxValue)] [RequestSizeLimit(long.MaxValue)]
[DisableRequestSizeLimit, RequestFormLimits(MultipartBodyLengthLimit = long.MaxValue, ValueLengthLimit = int.MaxValue)] [DisableRequestSizeLimit, RequestFormLimits(MultipartBodyLengthLimit = long.MaxValue, ValueLengthLimit = int.MaxValue)]
[Authorize(Roles = "Admin")]
public async Task<IActionResult> UploadPlatformMap(List<IFormFile> files) public async Task<IActionResult> UploadPlatformMap(List<IFormFile> files)
{ {
Guid sessionid = Guid.NewGuid(); Guid sessionid = Guid.NewGuid();
@@ -115,6 +122,7 @@ namespace gaseous_server.Controllers
} }
// [MapToApiVersion("1.0")] // [MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpPost] [HttpPost]
// [Route("{PlatformId}")] // [Route("{PlatformId}")]
// [ProducesResponseType(typeof(PlatformMapping.PlatformMapItem), StatusCodes.Status200OK)] // [ProducesResponseType(typeof(PlatformMapping.PlatformMapItem), StatusCodes.Status200OK)]
@@ -143,10 +151,12 @@ namespace gaseous_server.Controllers
// } // }
[MapToApiVersion("1.0")] [MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpPatch] [HttpPatch]
[Route("{PlatformId}")] [Route("{PlatformId}")]
[ProducesResponseType(typeof(PlatformMapping.PlatformMapItem), StatusCodes.Status200OK)] [ProducesResponseType(typeof(PlatformMapping.PlatformMapItem), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status404NotFound)]
[Authorize(Roles = "Admin")]
public ActionResult EditPlatformMap(long PlatformId, PlatformMapping.PlatformMapItem Map) public ActionResult EditPlatformMap(long PlatformId, PlatformMapping.PlatformMapItem Map)
{ {
try try

View File

@@ -9,6 +9,7 @@ using gaseous_server.Classes;
using gaseous_server.Classes.Metadata; using gaseous_server.Classes.Metadata;
using gaseous_server.Models; using gaseous_server.Models;
using IGDB.Models; using IGDB.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.CodeAnalysis.Scripting; using Microsoft.CodeAnalysis.Scripting;
@@ -17,10 +18,13 @@ namespace gaseous_server.Controllers
{ {
[Route("api/v{version:apiVersion}/[controller]")] [Route("api/v{version:apiVersion}/[controller]")]
[ApiVersion("1.0")] [ApiVersion("1.0")]
[ApiVersion("1.1")]
[Authorize]
[ApiController] [ApiController]
public class PlatformsController : Controller public class PlatformsController : Controller
{ {
[MapToApiVersion("1.0")] [MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[ProducesResponseType(typeof(List<Platform>), StatusCodes.Status200OK)] [ProducesResponseType(typeof(List<Platform>), StatusCodes.Status200OK)]
public ActionResult Platform() public ActionResult Platform()
@@ -46,6 +50,7 @@ namespace gaseous_server.Controllers
} }
[MapToApiVersion("1.0")] [MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[Route("{PlatformId}")] [Route("{PlatformId}")]
[ProducesResponseType(typeof(Platform), StatusCodes.Status200OK)] [ProducesResponseType(typeof(Platform), StatusCodes.Status200OK)]
@@ -72,6 +77,7 @@ namespace gaseous_server.Controllers
} }
[MapToApiVersion("1.0")] [MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[Route("{PlatformId}/platformlogo")] [Route("{PlatformId}/platformlogo")]
[ProducesResponseType(typeof(PlatformLogo), StatusCodes.Status200OK)] [ProducesResponseType(typeof(PlatformLogo), StatusCodes.Status200OK)]
@@ -105,6 +111,7 @@ namespace gaseous_server.Controllers
} }
[MapToApiVersion("1.0")] [MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[Route("{PlatformId}/platformlogo/image")] [Route("{PlatformId}/platformlogo/image")]
[ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)] [ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)]

View File

@@ -9,6 +9,7 @@ using System.Threading.Tasks;
using gaseous_server.Classes; using gaseous_server.Classes;
using gaseous_server.Classes.Metadata; using gaseous_server.Classes.Metadata;
using IGDB.Models; using IGDB.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.CodeAnalysis.Scripting; using Microsoft.CodeAnalysis.Scripting;
@@ -18,11 +19,15 @@ namespace gaseous_server.Controllers
{ {
[Route("api/v{version:apiVersion}/[controller]")] [Route("api/v{version:apiVersion}/[controller]")]
[ApiVersion("1.0")] [ApiVersion("1.0")]
[ApiVersion("1.1")]
[Authorize]
[ApiController] [ApiController]
public class RomsController : ControllerBase public class RomsController : ControllerBase
{ {
[MapToApiVersion("1.0")] [MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpPost] [HttpPost]
[Authorize(Roles = "Admin,Gamer")]
[ProducesResponseType(typeof(List<IFormFile>), StatusCodes.Status200OK)] [ProducesResponseType(typeof(List<IFormFile>), StatusCodes.Status200OK)]
[RequestSizeLimit(long.MaxValue)] [RequestSizeLimit(long.MaxValue)]
[DisableRequestSizeLimit, RequestFormLimits(MultipartBodyLengthLimit = long.MaxValue, ValueLengthLimit = int.MaxValue)] [DisableRequestSizeLimit, RequestFormLimits(MultipartBodyLengthLimit = long.MaxValue, ValueLengthLimit = int.MaxValue)]

View File

@@ -5,6 +5,7 @@ using System.Threading.Tasks;
using gaseous_server.Classes; using gaseous_server.Classes;
using IGDB; using IGDB;
using IGDB.Models; using IGDB.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using static gaseous_server.Classes.Metadata.Games; using static gaseous_server.Classes.Metadata.Games;
@@ -14,6 +15,8 @@ namespace gaseous_server.Controllers
[ApiController] [ApiController]
[Route("api/v{version:apiVersion}/[controller]")] [Route("api/v{version:apiVersion}/[controller]")]
[ApiVersion("1.0")] [ApiVersion("1.0")]
[ApiVersion("1.1")]
[Authorize]
public class SearchController : Controller public class SearchController : Controller
{ {
private static IGDBClient igdb = new IGDBClient( private static IGDBClient igdb = new IGDBClient(
@@ -23,6 +26,7 @@ namespace gaseous_server.Controllers
); );
[MapToApiVersion("1.0")] [MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[Route("Platform")] [Route("Platform")]
[ProducesResponseType(typeof(List<Platform>), StatusCodes.Status200OK)] [ProducesResponseType(typeof(List<Platform>), StatusCodes.Status200OK)]
@@ -45,6 +49,7 @@ namespace gaseous_server.Controllers
} }
[MapToApiVersion("1.0")] [MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[Route("Game")] [Route("Game")]
[ProducesResponseType(typeof(List<Game>), StatusCodes.Status200OK)] [ProducesResponseType(typeof(List<Game>), StatusCodes.Status200OK)]

View File

@@ -6,6 +6,7 @@ using System.Security.Cryptography;
using System.Threading.Tasks; using System.Threading.Tasks;
using gaseous_server.Classes; using gaseous_server.Classes;
using gaseous_signature_parser.models.RomSignatureObject; using gaseous_signature_parser.models.RomSignatureObject;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
// For more information on enabling MVC for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860 // 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] [ApiController]
[Route("api/v{version:apiVersion}/[controller]/[action]")] [Route("api/v{version:apiVersion}/[controller]/[action]")]
[ApiVersion("1.0")] [ApiVersion("1.0")]
[ApiVersion("1.1")]
[Authorize]
public class SignaturesController : ControllerBase public class SignaturesController : ControllerBase
{ {
/// <summary> /// <summary>
@@ -22,6 +25,7 @@ namespace gaseous_server.Controllers
/// </summary> /// </summary>
/// <returns>Number of sources, publishers, games, and rom signatures in the database</returns> /// <returns>Number of sources, publishers, games, and rom signatures in the database</returns>
[MapToApiVersion("1.0")] [MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status200OK)]
public Models.Signatures_Status Status() public Models.Signatures_Status Status()
@@ -30,6 +34,7 @@ namespace gaseous_server.Controllers
} }
[MapToApiVersion("1.0")] [MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status200OK)]
public List<Models.Signatures_Games> GetSignature(string md5 = "", string sha1 = "") public List<Models.Signatures_Games> GetSignature(string md5 = "", string sha1 = "")
@@ -44,6 +49,7 @@ namespace gaseous_server.Controllers
} }
[MapToApiVersion("1.0")] [MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status200OK)]
public List<Models.Signatures_Games> GetByTosecName(string TosecName = "") public List<Models.Signatures_Games> GetByTosecName(string TosecName = "")

View File

@@ -4,8 +4,11 @@ using System.Data;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Text; using System.Text;
using System.Text.Json;
using System.Threading.Tasks; using System.Threading.Tasks;
using gaseous_server.Classes; using gaseous_server.Classes;
using gaseous_server.Classes.Metadata;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
namespace gaseous_server.Controllers namespace gaseous_server.Controllers
@@ -13,9 +16,12 @@ namespace gaseous_server.Controllers
[ApiController] [ApiController]
[Route("api/v{version:apiVersion}/[controller]")] [Route("api/v{version:apiVersion}/[controller]")]
[ApiVersion("1.0")] [ApiVersion("1.0")]
[ApiVersion("1.1")]
[Authorize]
public class SystemController : Controller public class SystemController : Controller
{ {
[MapToApiVersion("1.0")] [MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status200OK)]
public SystemInfo GetSystemStatus() public SystemInfo GetSystemStatus()
@@ -56,6 +62,7 @@ namespace gaseous_server.Controllers
} }
[MapToApiVersion("1.0")] [MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[Route("Version")] [Route("Version")]
[ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status200OK)]
@@ -64,13 +71,30 @@ namespace gaseous_server.Controllers
} }
[MapToApiVersion("1.0")] [MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[Route("VersionFile")] [Route("VersionFile")]
[AllowAnonymous]
[ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status200OK)]
public FileContentResult GetSystemVersionAsFile() { public FileContentResult GetSystemVersionAsFile() {
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); 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 + 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); byte[] bytes = Encoding.UTF8.GetBytes(ver);
return File(bytes, "text/javascript"); return File(bytes, "text/javascript");
} }

View File

@@ -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();
}
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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();
}
}
}

View File

@@ -7,8 +7,12 @@ using gaseous_server.SignatureIngestors.XML;
using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Versioning; using Microsoft.AspNetCore.Mvc.Versioning;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Server.Kestrel.Core; using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Models;
using Authentication;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.UI.Services;
Logging.WriteToDiskOnly = true; Logging.WriteToDiskOnly = true;
Logging.Log(Logging.LogType.Information, "Startup", "Starting Gaseous Server " + Assembly.GetExecutingAssembly().GetName().Version); 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.DefaultApiVersion = new ApiVersion(1, 0);
config.AssumeDefaultVersionWhenUnspecified = true; config.AssumeDefaultVersionWhenUnspecified = true;
config.ReportApiVersions = true; config.ReportApiVersions = true;
config.ApiVersionReader = ApiVersionReader.Combine(new UrlSegmentApiVersionReader(),
new HeaderApiVersionReader("x-api-version"),
new MediaTypeApiVersionReader("x-api-version"));
}); });
builder.Services.AddApiVersioning(setup => // builder.Services.AddApiVersioning(setup =>
{ // {
setup.ApiVersionReader = new UrlSegmentApiVersionReader(); // setup.ApiVersionReader = new UrlSegmentApiVersionReader();
}); // });
builder.Services.AddVersionedApiExplorer(setup => builder.Services.AddVersionedApiExplorer(setup =>
{ {
setup.GroupNameFormat = "'v'VVV"; 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; // using System.Reflection;
var xmlFilename = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml"; var xmlFilename = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
options.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, xmlFilename)); options.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, xmlFilename));
@@ -173,19 +198,115 @@ builder.Services.AddSwaggerGen(options =>
); );
builder.Services.AddHostedService<TimedHostedService>(); 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(); var app = builder.Build();
// Configure the HTTP request pipeline. // Configure the HTTP request pipeline.
//if (app.Environment.IsDevelopment()) //if (app.Environment.IsDevelopment())
//{ //{
app.UseSwagger(); 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.UseHttpsRedirection();
app.UseResponseCaching(); 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.UseAuthorization();
app.UseDefaultFiles(); app.UseDefaultFiles();
@@ -197,6 +318,72 @@ app.UseStaticFiles(new StaticFileOptions
app.MapControllers(); 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 // setup library directories
Config.LibraryConfiguration.InitLibrary(); Config.LibraryConfiguration.InitLibrary();

View File

@@ -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
}
}

View File

@@ -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
}

View File

@@ -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
) ;

View File

@@ -20,9 +20,11 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="gaseous-signature-parser" Version="2.0.0" /> <PackageReference Include="gaseous-signature-parser" Version="2.0.0" />
<PackageReference Include="gaseous.IGDB" Version="1.0.1" /> <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" Version="5.1.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Versioning.ApiExplorer" 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.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="Swashbuckle.AspNetCore" Version="6.5.0" />
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="7.0.10" /> <PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="7.0.10" />
<PackageReference Include="MySqlConnector" Version="2.2.7" /> <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-1002.sql" />
<None Remove="Support\Database\MySQL\gaseous-1003.sql" /> <None Remove="Support\Database\MySQL\gaseous-1003.sql" />
<None Remove="Support\Database\MySQL\gaseous-1004.sql" /> <None Remove="Support\Database\MySQL\gaseous-1004.sql" />
<None Remove="Support\Database\MySQL\gaseous-1005.sql" />
<None Remove="Classes\Metadata\" /> <None Remove="Classes\Metadata\" />
<None Remove="Assets\" /> <None Remove="Assets\" />
<None Remove="Assets\Ratings\" /> <None Remove="Assets\Ratings\" />
@@ -114,6 +117,7 @@
<Folder Include="Assets\Ratings\USK\" /> <Folder Include="Assets\Ratings\USK\" />
<Folder Include="Assets\Ratings\GRAC\" /> <Folder Include="Assets\Ratings\GRAC\" />
<Folder Include="Assets\Ratings\CLASS_IND\" /> <Folder Include="Assets\Ratings\CLASS_IND\" />
<Folder Remove="Reference" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="wwwroot\**"> <None Include="wwwroot\**">
@@ -169,5 +173,6 @@
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1002.sql" /> <EmbeddedResource Include="Support\Database\MySQL\gaseous-1002.sql" />
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1003.sql" /> <EmbeddedResource Include="Support\Database\MySQL\gaseous-1003.sql" />
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1004.sql" /> <EmbeddedResource Include="Support\Database\MySQL\gaseous-1004.sql" />
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1005.sql" />
</ItemGroup> </ItemGroup>
</Project> </Project>

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 592 KiB

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

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

View File

@@ -2,7 +2,7 @@
<html> <html>
<head> <head>
<meta charset="utf-8" /> <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" /> <link type="text/css" rel="stylesheet" dat-href="/styles/style.css" />
<script src="/scripts/jquery-3.6.0.min.js"></script> <script src="/scripts/jquery-3.6.0.min.js"></script>
<script src="/scripts/moment.js"></script> <script src="/scripts/moment.js"></script>
@@ -38,6 +38,8 @@
head.appendChild(newLink); head.appendChild(newLink);
} }
} }
var userProfile;
</script> </script>
</head> </head>
<body> <body>
@@ -45,24 +47,35 @@
<img src="/images/logo.png" alt="Gaseous" id="banner_icon_image" /> <img src="/images/logo.png" alt="Gaseous" id="banner_icon_image" />
</div> </div>
<div id="banner_header"> <div id="banner_header">
<div id="banner_cog" onclick="window.location.href = '/index.html?page=settings';" class="banner_button"> <div id="bannerButtons">
<img src="/images/settings.svg" alt="Settings" title="Settings" id="banner_system_image" class="banner_button_image" /> <div id="banner_user" onclick="showMenu();" class="banner_button dropdown dropbtn">
<span id="banner_system_label">Settings</span> <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> <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"> <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" /> <img src="/images/upload.svg" alt="Upload" title="Upload" id="banner_upload_image" class="banner_button_image" />
<span id="banner_upload_label">Upload</span> <span id="banner_upload_label">Upload</span>
</div> </div>
<div id="banner_collections" onclick="window.location.href = '/index.html?page=collections';" class="banner_button"> <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" /> <img src="/images/collections.svg" alt="Collections" title="Collections" id="banner_collections_image" class="banner_button_image" />
<span id="banner_collections_label">Collections</span> <span id="banner_collections_label">Collections</span>
</div> </div>
<div id="banner_library" onclick="window.location.href = '/index.html';" class="banner_button"> <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" /> <img src="/images/library.svg" alt="Library" title="Library" id="banner_library_image" class="banner_button_image" />
<span id="banner_library_label">Library</span> <span id="banner_library_label">Library</span>
</div>
</div> </div>
<div id="banner_header_label" onclick="window.location.href = '/index.html';">Gaseous Games</div> <div id="banner_header_label" onclick="window.location.href = '/index.html';">Gaseous Games</div>
@@ -96,18 +109,74 @@
</div> </div>
<script type="text/javascript">var modalVariables = null; <script type="text/javascript">
var modalVariables = null;
$(document).ready(function () { // redirect if first run status = 0
var myParam = getQueryString('page', 'string'); if (FirstRunStatus == 0) {
window.location.replace("/pages/first.html");
}
if (!myParam) { // redirect if not logged in
myParam = 'home'; 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> </script>
</body> </body>
</html> </html>

View File

@@ -17,10 +17,17 @@
</div> </div>
<script type="text/javascript"> <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(); GetCollections();
function GetCollections() { function GetCollections() {
ajaxCall('/api/v1.0/Collections', 'GET', function (result) { ajaxCall('/api/v1.1/Collections', 'GET', function (result) {
if (result) { if (result) {
var targetDiv = document.getElementById('collection_table_location'); var targetDiv = document.getElementById('collection_table_location');
targetDiv.innerHTML = ''; targetDiv.innerHTML = '';
@@ -47,7 +54,7 @@
break; break;
case "Completed": case "Completed":
statusText = 'Available'; 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); packageSize = formatBytes(result[i].collectionBuiltSizeBytes);
break; break;
case "Failed": case "Failed":
@@ -58,9 +65,14 @@
break; 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 = [ var newRow = [
result[i].name, result[i].name,

View File

@@ -24,7 +24,7 @@
$('#collection_addgame').select2({ $('#collection_addgame').select2({
ajax: { ajax: {
url: '/api/v1.0/Collections', url: '/api/v1.1/Collections',
placeholder: 'Select collection', placeholder: 'Select collection',
processResults: function (data) { processResults: function (data) {
var arr = []; var arr = [];
@@ -59,7 +59,7 @@
if (CollectionId != 0) { if (CollectionId != 0) {
ajaxCall( ajaxCall(
'/api/v1.0/Collections/' + CollectionId + '/AlwaysInclude' + RebuildCollection, '/api/v1.1/Collections/' + CollectionId + '/AlwaysInclude' + RebuildCollection,
'PATCH', 'PATCH',
function (result) { function (result) {
closeSubDialog(); closeSubDialog();

View File

@@ -12,7 +12,7 @@
<script type="text/javascript"> <script type="text/javascript">
function deleteCollection() { function deleteCollection() {
ajaxCall( ajaxCall(
'/api/v1.0/Collections/' + subModalVariables, '/api/v1.1/Collections/' + subModalVariables,
'DELETE', 'DELETE',
function (result) { function (result) {
GetCollections(); GetCollections();

View File

@@ -136,7 +136,7 @@
// setup dropdowns // setup dropdowns
$('#collection_platform').select2({ $('#collection_platform').select2({
ajax: { ajax: {
url: '/api/v1.0/Filter', url: '/api/v1.1/Filter',
processResults: function (data) { processResults: function (data) {
var filter = data['platforms']; var filter = data['platforms'];
@@ -159,7 +159,7 @@
$('#collection_genres').select2({ $('#collection_genres').select2({
ajax: { ajax: {
url: '/api/v1.0/Filter', url: '/api/v1.1/Filter',
processResults: function (data) { processResults: function (data) {
var filter = data['genres']; var filter = data['genres'];
@@ -182,7 +182,7 @@
$('#collection_players').select2({ $('#collection_players').select2({
ajax: { ajax: {
url: '/api/v1.0/Filter', url: '/api/v1.1/Filter',
processResults: function (data) { processResults: function (data) {
var filter = data['gamemodes']; var filter = data['gamemodes'];
@@ -205,7 +205,7 @@
$('#collection_playerperspectives').select2({ $('#collection_playerperspectives').select2({
ajax: { ajax: {
url: '/api/v1.0/Filter', url: '/api/v1.1/Filter',
processResults: function (data) { processResults: function (data) {
var filter = data['playerperspectives']; var filter = data['playerperspectives'];
@@ -228,7 +228,7 @@
$('#collection_themes').select2({ $('#collection_themes').select2({
ajax: { ajax: {
url: '/api/v1.0/Filter', url: '/api/v1.1/Filter',
processResults: function (data) { processResults: function (data) {
var filter = data['themes']; var filter = data['themes'];
@@ -258,7 +258,7 @@
// edit mode // edit mode
ajaxCall( ajaxCall(
'/api/v1.0/Collections/' + modalVariables, '/api/v1.1/Collections/' + modalVariables,
'GET', 'GET',
function(result) { function(result) {
if (result.name) { document.getElementById('collection_name').value = result.name; } if (result.name) { document.getElementById('collection_name').value = result.name; }
@@ -278,7 +278,7 @@
// fill select2 controls // fill select2 controls
$.ajax( $.ajax(
{ {
url: '/api/v1.0/Filter', url: '/api/v1.1/Filter',
type: 'GET', type: 'GET',
indexValue: result, indexValue: result,
dataType: 'json', dataType: 'json',
@@ -344,7 +344,7 @@
// existing object - save over the top // existing object - save over the top
item.id = modalVariables; item.id = modalVariables;
ajaxCall( ajaxCall(
'/api/v1.0/Collections/' + modalVariables, '/api/v1.1/Collections/' + modalVariables,
'PATCH', 'PATCH',
function(result) { function(result) {
location.reload(); location.reload();
@@ -357,7 +357,7 @@
} else { } else {
// new object // new object
ajaxCall( ajaxCall(
'/api/v1.0/Collections', '/api/v1.1/Collections',
'POST', 'POST',
function(result) { function(result) {
location.reload(); location.reload();
@@ -426,7 +426,7 @@
var item = GenerateCollectionItem(); var item = GenerateCollectionItem();
ajaxCall( ajaxCall(
'/api/v1.0/Collections/Preview', '/api/v1.1/Collections/Preview',
'POST', 'POST',
function(result) { function(result) {
DisplayPreview(result, 'collectionedit_previewbox_content'); DisplayPreview(result, 'collectionedit_previewbox_content');
@@ -579,7 +579,7 @@
var gameImage = document.createElement('img'); var gameImage = document.createElement('img');
gameImage.className = 'game_tile_image game_tile_image_small'; gameImage.className = 'game_tile_image game_tile_image_small';
if (gameItem.cover) { if (gameItem.cover) {
gameImage.src = '/api/v1.0/Games/' + gameItem.id + '/cover/image'; gameImage.src = '/api/v1.1/Games/' + gameItem.id + '/cover/image';
} else { } else {
gameImage.src = '/images/unknowngame.png'; gameImage.src = '/images/unknowngame.png';
gameImage.className = 'game_tile_image game_tile_image_small unknown'; gameImage.className = 'game_tile_image game_tile_image_small unknown';

View File

@@ -12,7 +12,7 @@
<script type="text/javascript"> <script type="text/javascript">
function deleteLibrary() { function deleteLibrary() {
ajaxCall( ajaxCall(
'/api/v1.0/Library/' + subModalVariables, '/api/v1.1/Library/' + subModalVariables,
'DELETE', 'DELETE',
function (result) { function (result) {
drawLibrary(); drawLibrary();

View File

@@ -32,7 +32,7 @@
$('#newlibrary_defaultplatform').select2({ $('#newlibrary_defaultplatform').select2({
minimumInputLength: 3, minimumInputLength: 3,
ajax: { ajax: {
url: '/api/v1.0/Search/Platform', url: '/api/v1.1/Search/Platform',
data: function (params) { data: function (params) {
var query = { var query = {
SearchString: params.term SearchString: params.term
@@ -76,7 +76,7 @@
alert("A path must be provided."); alert("A path must be provided.");
} else { } else {
ajaxCall( 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', 'POST',
function(result) { function(result) {
drawLibrary(); drawLibrary();

View File

@@ -12,7 +12,7 @@
<script type="text/javascript"> <script type="text/javascript">
function deleteCollection() { function deleteCollection() {
ajaxCall( ajaxCall(
'/api/v1.0/Games/' + gameData.id + '/romgroup/' + subModalVariables, '/api/v1.1/Games/' + gameData.id + '/romgroup/' + subModalVariables,
'DELETE', 'DELETE',
function (result) { function (result) {
loadRoms(); loadRoms();

View File

@@ -105,7 +105,7 @@
DisplayWebEmulatorContent(false); DisplayWebEmulatorContent(false);
ajaxCall( ajaxCall(
'/api/v1.0/PlatformMaps/' + modalVariables, '/api/v1.1/PlatformMaps/' + modalVariables,
'GET', 'GET',
function (result) { function (result) {
// set heading // set heading
@@ -338,7 +338,7 @@
console.log(JSON.stringify(item)); console.log(JSON.stringify(item));
ajaxCall( ajaxCall(
'/api/v1.0/PlatformMaps/' + modalVariables, '/api/v1.1/PlatformMaps/' + modalVariables,
'PATCH', 'PATCH',
function (result) { function (result) {
loadPlatformMapping(); loadPlatformMapping();

View File

@@ -11,7 +11,7 @@
<script type="text/javascript"> <script type="text/javascript">
function deleteRom() { 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(); window.location.reload();
}); });
} }

View File

@@ -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; romData = result;
document.getElementById('modal-heading').innerHTML = result.name; document.getElementById('modal-heading').innerHTML = result.name;
document.getElementById('rominfo_library').innerHTML = result.library.name; document.getElementById('rominfo_library').innerHTML = result.library.name;
@@ -169,7 +169,7 @@
$('#properties_fixplatform').select2({ $('#properties_fixplatform').select2({
minimumInputLength: 3, minimumInputLength: 3,
ajax: { ajax: {
url: '/api/v1.0/Search/Platform', url: '/api/v1.1/Search/Platform',
data: function (params) { data: function (params) {
var query = { var query = {
SearchString: params.term SearchString: params.term
@@ -200,7 +200,7 @@
minimumInputLength: 3, minimumInputLength: 3,
templateResult: DropDownRenderGameOption, templateResult: DropDownRenderGameOption,
ajax: { ajax: {
url: '/api/v1.0/Search/Game', url: '/api/v1.1/Search/Game',
data: function (params) { data: function (params) {
fixplatform = $('#properties_fixplatform').select2('data'); fixplatform = $('#properties_fixplatform').select2('data');
@@ -236,7 +236,7 @@
document.getElementById('properties_fixsave').setAttribute("disabled", "disabled"); 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(); window.location.reload();
}); });
} }

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -28,7 +28,7 @@
document.getElementById('upload_platformoverride').innerHTML = "<option value='0' selected='selected'>Automatic Platform</option>"; document.getElementById('upload_platformoverride').innerHTML = "<option value='0' selected='selected'>Automatic Platform</option>";
var myDropzone = new Dropzone("div#upload_target", { var myDropzone = new Dropzone("div#upload_target", {
url: "/api/v1.0/Roms", url: "/api/v1.1/Roms",
autoProcessQueue: true, autoProcessQueue: true,
uploadMultiple: true, uploadMultiple: true,
paramName: myParamName, paramName: myParamName,
@@ -84,7 +84,7 @@
$('#upload_platformoverride').select2({ $('#upload_platformoverride').select2({
minimumInputLength: 3, minimumInputLength: 3,
ajax: { ajax: {
url: '/api/v1.0/Search/Platform', url: '/api/v1.1/Search/Platform',
data: function (params) { data: function (params) {
var query = { var query = {
SearchString: params.term SearchString: params.term
@@ -125,6 +125,6 @@
} }
console.log(queryString); console.log(queryString);
myDropzone.options.url = "/api/v1.0/Roms" + queryString; myDropzone.options.url = "/api/v1.1/Roms" + queryString;
}); });
</script> </script>

View File

@@ -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>

View File

@@ -15,7 +15,7 @@
var emuBios = ''; var emuBios = '';
var emuBackground = ''; var emuBackground = '';
ajaxCall('/api/v1.0/Games/' + gameId, 'GET', function (result) { ajaxCall('/api/v1.1/Games/' + gameId, 'GET', function (result) {
gameData = result; gameData = result;
// load artwork // load artwork
@@ -27,22 +27,22 @@
} else { } else {
if (result.cover) { if (result.cover) {
var bg = document.getElementById('bgImage'); 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) { if (result.cover) {
emuBackground = '/api/v1.0/Games/' + gameId + '/cover/image'; emuBackground = '/api/v1.1/Games/' + gameId + '/cover/image';
} }
emuGameTitle = gameData.name; 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) { if (result.length == 0) {
emuBios = ''; emuBios = '';
} else { } else {
emuBios = '/api/v1.0/Bios/zip/' + platformId; emuBios = '/api/v1.1/Bios/zip/' + platformId;
} }
switch (getQueryString('engine', 'string')) { switch (getQueryString('engine', 'string')) {
@@ -59,7 +59,7 @@
artworksPosition = 0; artworksPosition = 0;
} }
var bg = document.getElementById('bgImage'); 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> </script>

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>

View File

@@ -94,7 +94,7 @@
var artworksTimer = null; var artworksTimer = null;
var selectedScreenshot = 0; var selectedScreenshot = 0;
ajaxCall('/api/v1.0/Games/' + gameId, 'GET', function (result) { ajaxCall('/api/v1.1/Games/' + gameId, 'GET', function (result) {
// populate games page // populate games page
gameData = result; gameData = result;
@@ -116,7 +116,7 @@
// get alt name // get alt name
var gameTitleAltLabel = document.getElementById('gametitle_alts'); var gameTitleAltLabel = document.getElementById('gametitle_alts');
if (result.alternativeNames) { 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 = ''; var altNames = '';
for (var i = 0; i < result.length; i++) { for (var i = 0; i < result.length; i++) {
if (altNames.length > 0) { if (altNames.length > 0) {
@@ -161,7 +161,7 @@
} else { } else {
var bg = document.getElementById('bgImage'); var bg = document.getElementById('bgImage');
if (result.cover) { 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 { } else {
var randomInt = randomIntFromInterval(1, 3); 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);'); 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 gameDeveloperLoaded = false;
var gamePublisherLoaded = false; var gamePublisherLoaded = false;
if (result.involvedCompanies) { 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 lstDevelopers = [];
var lstPublishers = []; var lstPublishers = [];
@@ -227,7 +227,7 @@
var gameImage = document.createElement('img'); var gameImage = document.createElement('img');
gameImage.className = 'game_cover_image'; gameImage.className = 'game_cover_image';
if (result.cover) { if (result.cover) {
gameImage.src = '/api/v1.0/Games/' + result.id + '/cover/image'; gameImage.src = '/api/v1.1/Games/' + result.id + '/cover/image';
} else { } else {
gameImage.src = '/images/unknowngame.png'; gameImage.src = '/images/unknowngame.png';
gameImage.className = 'game_cover_image unknown'; gameImage.className = 'game_cover_image unknown';
@@ -240,7 +240,7 @@
var gameRatings = document.createElement('div'); var gameRatings = document.createElement('div');
for (var i = 0; i < result.ageRatings.ids.length; i++) { for (var i = 0; i < result.ageRatings.ids.length; i++) {
var ratingImage = document.createElement('img'); 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'; ratingImage.className = 'rating_image';
gameRatings.appendChild(ratingImage); gameRatings.appendChild(ratingImage);
} }
@@ -252,7 +252,7 @@
// load genres // load genres
var gameSummaryGenres = document.getElementById('gamesumarry_genres'); var gameSummaryGenres = document.getElementById('gamesumarry_genres');
if (result.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++) { for (var i = 0; i < result.length; i++) {
var genreLabel = document.createElement('span'); var genreLabel = document.createElement('span');
genreLabel.className = 'gamegenrelabel'; genreLabel.className = 'gamegenrelabel';
@@ -281,7 +281,7 @@
var screenshotItem = document.createElement('div'); var screenshotItem = document.createElement('div');
screenshotItem.id = 'gamescreenshots_gallery_' + imageIndex; screenshotItem.id = 'gamescreenshots_gallery_' + imageIndex;
screenshotItem.setAttribute('name', 'gamescreenshots_gallery_item'); 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('imageid', imageIndex);
screenshotItem.setAttribute('imagetype', 0); screenshotItem.setAttribute('imagetype', 0);
screenshotItem.className = 'gamescreenshots_gallery_item'; screenshotItem.className = 'gamescreenshots_gallery_item';
@@ -293,7 +293,7 @@
// load videos // load videos
if (result.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'); var gameScreenshots_vGallery = document.getElementById('gamescreenshots_gallery_panel');
for (var i = 0; i < result.length; i++) { for (var i = 0; i < result.length; i++) {
var vScreenshotItem = document.createElement('div'); var vScreenshotItem = document.createElement('div');
@@ -360,7 +360,7 @@
} }
var gameRoms = document.getElementById('gamesummaryroms'); 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) { if (result.gameRomItems) {
var gameRomItems = result.gameRomItems; var gameRomItems = result.gameRomItems;
var mediaGroups = result.mediaGroups; var mediaGroups = result.mediaGroups;
@@ -391,14 +391,14 @@
if (result.gameRomItems[i].emulator) { if (result.gameRomItems[i].emulator) {
if (gameRomItems[i].emulator.type) { if (gameRomItems[i].emulator.type) {
if (gameRomItems[i].emulator.type.length > 0) { 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 = [ 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'], ['<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), formatBytes(gameRomItems[i].size, 2),
gameRomItems[i].romTypeMedia, gameRomItems[i].romTypeMedia,
gameRomItems[i].mediaLabel, gameRomItems[i].mediaLabel,
@@ -436,7 +436,7 @@
if (gameRomItem.platformId == mediaGroup.platformId) { if (gameRomItem.platformId == mediaGroup.platformId) {
if (gameRomItem.emulator) { if (gameRomItem.emulator) {
if (gameRomItem.emulator.type.length > 0) { 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; break;
} }
} }
@@ -459,7 +459,7 @@
break; break;
case "Completed": case "Completed":
statusText = 'Available'; 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); packageSize = formatBytes(mediaGroup.size);
launchButtonContent = launchButton; launchButtonContent = launchButton;
break; break;
@@ -525,7 +525,7 @@
artworksPosition = 0; artworksPosition = 0;
} }
var bg = document.getElementById('bgImage'); 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); artworksTimer = setTimeout(rotateBackground, 60000);
} }
} }
@@ -677,7 +677,7 @@
minimumInputLength: 3, minimumInputLength: 3,
placeholder: "Platform", placeholder: "Platform",
ajax: { ajax: {
url: '/api/v1.0/Search/Platform', url: '/api/v1.1/Search/Platform',
data: function (params) { data: function (params) {
var query = { var query = {
SearchString: params.term SearchString: params.term
@@ -709,7 +709,7 @@
templateResult: DropDownRenderGameOption, templateResult: DropDownRenderGameOption,
placeholder: "Game", placeholder: "Game",
ajax: { ajax: {
url: '/api/v1.0/Search/Game', url: '/api/v1.1/Search/Game',
data: function (params) { data: function (params) {
fixplatform = $('#rom_edit_fixplatform').select2('data'); fixplatform = $('#rom_edit_fixplatform').select2('data');
@@ -762,7 +762,7 @@
if (rom_checks[i].checked == true) { if (rom_checks[i].checked == true) {
var romId = rom_checks[i].getAttribute('data-romid'); var romId = rom_checks[i].getAttribute('data-romid');
remapCallCounter += 1; 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(); remapTitlesCallback();
}, function (result) { }, function (result) {
remapTitlesCallback(); remapTitlesCallback();
@@ -804,7 +804,7 @@
if (rom_checks[i].checked == true) { if (rom_checks[i].checked == true) {
var romId = rom_checks[i].getAttribute('data-romid'); var romId = rom_checks[i].getAttribute('data-romid');
remapCallCounter += 1; 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(); remapTitlesCallback();
}); });
} }
@@ -846,7 +846,7 @@
} }
ajaxCall( ajaxCall(
'/api/v1.0/Games/' + gameId + '/romgroup?PlatformId=' + platformId, '/api/v1.1/Games/' + gameId + '/romgroup?PlatformId=' + platformId,
'POST', 'POST',
function (result) { function (result) {
DisplayROMCheckboxes(false); DisplayROMCheckboxes(false);

View File

@@ -8,10 +8,10 @@
</div> </div>
<script type="text/javascript"> <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'); var filterElement = document.getElementById('games_filter');
formatFilterPanel(filterElement, result); formatFilterPanel(filterElement, result);
executeFilter(); executeFilter1_1();
}); });
</script> </script>

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>

View File

@@ -6,10 +6,11 @@
<div id="properties_toc" class="settings_toc"> <div id="properties_toc" class="settings_toc">
<div class="filter_header">Settings</div> <div class="filter_header">Settings</div>
<div id="properties_toc_system" name="properties_toc_item" onclick="SelectTab('system');">System</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_settings" name="properties_toc_item" onclick="SelectTab('settings');" style="display: none;">Settings</div>
<div id="properties_toc_mapping" name="properties_toc_item" onclick="SelectTab('mapping');">Platform Mapping</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_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 id="properties_toc_about" name="properties_toc_item" onclick="SelectTab('about');">About</div>
</div> </div>
<div id="properties_bodypanel"> <div id="properties_bodypanel">
@@ -23,6 +24,16 @@
</div> </div>
<script type="text/javascript"> <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 myParam = getQueryString('sub', 'string');
var selectedTab = ''; var selectedTab = '';

View File

@@ -8,7 +8,7 @@
</table> </table>
<script type="text/javascript"> <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)); result.sort((a, b) => a.platformname.charCodeAt(0) - b.platformname.charCodeAt(0));
var lastPlatform = ''; var lastPlatform = '';
@@ -28,7 +28,7 @@
} }
var biosFilename = document.createElement('a'); 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.innerHTML = result[i].filename;
biosFilename.className = 'romlink'; biosFilename.className = 'romlink';
@@ -38,7 +38,7 @@
availableText.className = 'greentext'; availableText.className = 'greentext';
biosFilename = document.createElement('a'); 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.innerHTML = result[i].filename;
biosFilename.className = 'romlink'; biosFilename.className = 'romlink';
} else { } else {

View File

@@ -26,7 +26,7 @@
} }
ajaxCall( ajaxCall(
'/api/v1.0/Logs' + apiQuery, '/api/v1.1/Logs' + apiQuery,
'GET', 'GET',
function (result) { function (result) {
var newTable = document.getElementById('settings_events_table'); var newTable = document.getElementById('settings_events_table');

View File

@@ -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>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/> <input id='uploadjson' type='file' name='files' hidden/>
@@ -15,6 +15,10 @@
</table> </table>
<script type="text/javascript"> <script type="text/javascript">
if (userProfile.roles.includes("Admin")) {
document.getElementById('settings_mapping_import').style.display = '';
}
function loadPlatformMapping(Overwrite) { function loadPlatformMapping(Overwrite) {
var queryString = ''; var queryString = '';
if (Overwrite == true) { if (Overwrite == true) {
@@ -22,7 +26,7 @@
} }
ajaxCall( ajaxCall(
'/api/v1.0/PlatformMaps' + queryString, '/api/v1.1/PlatformMaps' + queryString,
'GET', 'GET',
function (result) { function (result) {
var newTable = document.getElementById('settings_mapping_table'); var newTable = document.getElementById('settings_mapping_table');
@@ -47,8 +51,15 @@
hasWebEmulator = 'Yes'; 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 = [ var newRow = [
'<a href="#/" onclick="ShowPlatformMappingDialog(' + result[i].igdbId + ');" class="romlink">' + result[i].igdbName + '</a>', platformLink,
result[i].extensions.supportedFileExtensions.join(', '), result[i].extensions.supportedFileExtensions.join(', '),
result[i].extensions.uniqueFileExtensions.join(', '), result[i].extensions.uniqueFileExtensions.join(', '),
hasWebEmulator hasWebEmulator
@@ -61,7 +72,7 @@
} }
function DownloadJSON() { function DownloadJSON() {
window.open('/api/v1.0/PlatformMaps', '_blank'); window.open('/api/v1.1/PlatformMaps', '_blank');
} }
document.getElementById('importjson').addEventListener('click', openDialog); document.getElementById('importjson').addEventListener('click', openDialog);
@@ -71,7 +82,7 @@
} }
$('#uploadjson').change(function () { $('#uploadjson').change(function () {
$(this).simpleUpload("/api/v1.0/PlatformMaps", { $(this).simpleUpload("/api/v1.1/PlatformMaps", {
start: function (file) { start: function (file) {
//upload started //upload started
console.log("JSON upload started"); console.log("JSON upload started");

View File

@@ -11,7 +11,7 @@
<script type="text/javascript"> <script type="text/javascript">
function drawLibrary() { function drawLibrary() {
ajaxCall( ajaxCall(
'/api/v1.0/Library', '/api/v1.1/Library',
'GET', 'GET',
function (result) { function (result) {
var newTable = document.getElementById('settings_libraries'); var newTable = document.getElementById('settings_libraries');

View File

@@ -26,7 +26,7 @@
<div id="system_signatures"></div> <div id="system_signatures"></div>
<script type="text/javascript">function SystemLoadStatus() { <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'); var newTable = document.createElement('table');
newTable.className = 'romtable'; newTable.className = 'romtable';
newTable.setAttribute('cellspacing', 0); newTable.setAttribute('cellspacing', 0);
@@ -91,8 +91,10 @@
var itemInterval = result[i].interval; var itemInterval = result[i].interval;
var nextRunTime = moment(result[i].nextRunTime).fromNow(); var nextRunTime = moment(result[i].nextRunTime).fromNow();
var startButton = ''; var startButton = '';
if (result[i].allowManualStart == true && result[i].itemState != "Running") { if (userProfile.roles.includes("Admin")) {
startButton = "<span id='startProcess' class='romstart' onclick='StartProcess(\"" + result[i].itemType + "\");'>Start</span>"; 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) { if (result[i].allowManualStart == false && result[i].removeWhenStopped == true) {
@@ -119,7 +121,7 @@
} }
function SystemLoadSystemStatus() { function SystemLoadSystemStatus() {
ajaxCall('/api/v1.0/System', 'GET', function (result) { ajaxCall('/api/v1.1/System', 'GET', function (result) {
if (result) { if (result) {
var totalLibrarySpace = 0; var totalLibrarySpace = 0;
@@ -234,7 +236,7 @@
} }
function SystemSignaturesStatus() { 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'); var newTable = document.createElement('table');
newTable.className = 'romtable'; newTable.className = 'romtable';
newTable.setAttribute('cellspacing', 0); newTable.setAttribute('cellspacing', 0);
@@ -257,7 +259,7 @@
} }
function StartProcess(itemType) { 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(); SystemLoadStatus();
}); });
} }

View File

@@ -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>

View File

@@ -171,7 +171,7 @@ function buildFilterPanelItem(filterType, itemString, friendlyItemString, tags)
filterPanelItemCheckBoxItem.className = 'filter_panel_item_checkbox'; filterPanelItemCheckBoxItem.className = 'filter_panel_item_checkbox';
filterPanelItemCheckBoxItem.name = 'filter_' + filterType; filterPanelItemCheckBoxItem.name = 'filter_' + filterType;
filterPanelItemCheckBoxItem.setAttribute('filter_id', itemString); filterPanelItemCheckBoxItem.setAttribute('filter_id', itemString);
filterPanelItemCheckBoxItem.setAttribute('oninput' , 'executeFilter();'); filterPanelItemCheckBoxItem.setAttribute('oninput' , 'executeFilter1_1();');
if (checkState == true) { if (checkState == true) {
filterPanelItemCheckBoxItem.checked = true; filterPanelItemCheckBoxItem.checked = true;
} }
@@ -198,7 +198,7 @@ function executeFilterDelayed() {
filterExecutor = null; filterExecutor = null;
} }
filterExecutor = setTimeout(executeFilter, 1000); filterExecutor = setTimeout(executeFilter1_1, 1000);
} }
function executeFilter() { function executeFilter() {
@@ -299,4 +299,82 @@ function buildFilterTag(tags) {
} }
return boundingDiv; 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;
} }

View File

@@ -20,7 +20,7 @@ function renderGameIcon(gameObject, showTitle, showRatings) {
var gameImage = document.createElement('img'); var gameImage = document.createElement('img');
gameImage.className = 'game_tile_image lazy'; gameImage.className = 'game_tile_image lazy';
if (gameObject.cover) { 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 { } else {
gameImage.src = '/images/unknowngame.png'; gameImage.src = '/images/unknowngame.png';
gameImage.className = 'game_tile_image unknown'; gameImage.className = 'game_tile_image unknown';
@@ -40,7 +40,7 @@ function renderGameIcon(gameObject, showTitle, showRatings) {
ratingsSection.id = 'ratings_section'; ratingsSection.id = 'ratings_section';
for (var i = 0; i < gameObject.ageRatings.ids.length; i++) { for (var i = 0; i < gameObject.ageRatings.ids.length; i++) {
var ratingImage = document.createElement('img'); 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'; ratingImage.className = 'rating_image_mini';
ratingsSection.appendChild(ratingImage); ratingsSection.appendChild(ratingImage);
} }

View File

@@ -373,4 +373,19 @@ function LoadEditableTableData(TableName, Headers, Values) {
eTable.appendChild(row); 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;
} }

View File

@@ -30,12 +30,12 @@ h3 {
.modal { .modal {
display: none; /* Hidden by default */ display: none; /* Hidden by default */
position: fixed; /* Stay in place */ position: fixed; /* Stay in place */
z-index: 1; /* Sit on top */ z-index: 100; /* Sit on top */
left: 0; left: 0;
top: 0; top: 0;
width: 100%; /* Full width */ width: 100%; /* Full width */
height: 100%; /* Full height */ 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: rgb(0,0,0); /* Fallback color */
background-color: rgba(0,0,0,0.4); /* Black w/ opacity */ background-color: rgba(0,0,0,0.4); /* Black w/ opacity */
backdrop-filter: blur(8px); backdrop-filter: blur(8px);
@@ -47,9 +47,10 @@ h3 {
/* Modal Content/Box */ /* Modal Content/Box */
.modal-content { .modal-content {
background-color: #383838; background-color: #383838;
margin: 15% auto; /* 15% from the top and centered */ margin: 10% auto; /* 15% from the top and centered */
padding: 10px; padding: 10px;
border: 1px solid #888; border: 1px solid #888;
border-radius: 10px;
width: 700px; /* Could be more or less, depending on screen size */ width: 700px; /* Could be more or less, depending on screen size */
min-height: 358px; min-height: 358px;
} }
@@ -58,6 +59,7 @@ h3 {
margin: 20% auto; /* 20% from the top and centered */ margin: 20% auto; /* 20% from the top and centered */
padding: 10px; padding: 10px;
border: 1px solid #888; border: 1px solid #888;
border-radius: 10px;
width: 300px; /* Could be more or less, depending on screen size */ width: 300px; /* Could be more or less, depending on screen size */
min-height: 110px; min-height: 110px;
} }
@@ -205,7 +207,7 @@ h3 {
z-index: 1; z-index: 1;
} }
input[type='text'], input[type='number'] { input[type='text'], input[type='number'], input[type="email"], input[type="password"] {
background-color: #2b2b2b; background-color: #2b2b2b;
color: white; color: white;
padding: 5px; padding: 5px;
@@ -409,7 +411,7 @@ input[id='filter_panel_userrating_max'] {
display: inline-block; display: inline-block;
max-width: 32px; max-width: 32px;
max-height: 32px; max-height: 32px;
margin-right: 2px; margin-right: 10px;
} }
#gamescreenshots { #gamescreenshots {
@@ -621,14 +623,14 @@ th {
height: 100%; 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; padding: 10px;
border-bottom-width: 1px; border-bottom-width: 1px;
border-bottom-style: solid; border-bottom-style: solid;
border-bottom-color: #2b2b2b; 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; background-color: #2b2b2b;
cursor: pointer; cursor: pointer;
} }
@@ -965,4 +967,98 @@ button:disabled {
.romGroupTitles { .romGroupTitles {
padding-left: 20px; 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;
} }