mirror of
https://github.com/hargata/lubelog.git
synced 2026-02-03 17:53:02 -06:00
logic layer to manage api keys
This commit is contained in:
parent
8151747b66
commit
9403ea437e
@ -76,7 +76,8 @@ namespace CarCareTracker.Controllers
|
||||
&& _configHelper.DeleteUserConfig(userId)
|
||||
&& _loginLogic.DeleteUser(userId)
|
||||
&& _userLogic.DeleteAllHouseholdByChildUserId(userId)
|
||||
&& _userLogic.DeleteAllHouseholdByParentUserId(userId);
|
||||
&& _userLogic.DeleteAllHouseholdByParentUserId(userId)
|
||||
&& _userLogic.DeleteAllAPIKeysByUserId(userId);
|
||||
return Json(result);
|
||||
}
|
||||
[HttpPost]
|
||||
|
||||
62
External/Implementations/Litedb/ApiKeyRecordDataAccess.cs
vendored
Normal file
62
External/Implementations/Litedb/ApiKeyRecordDataAccess.cs
vendored
Normal file
@ -0,0 +1,62 @@
|
||||
using CarCareTracker.External.Interfaces;
|
||||
using CarCareTracker.Models;
|
||||
using LiteDB;
|
||||
using CarCareTracker.Helper;
|
||||
|
||||
namespace CarCareTracker.External.Implementations
|
||||
{
|
||||
public class ApiKeyRecordDataAccess : IApiKeyRecordDataAccess
|
||||
{
|
||||
private ILiteDBHelper _liteDB { get; set; }
|
||||
private static string tableName = "apikeyrecords";
|
||||
public ApiKeyRecordDataAccess(ILiteDBHelper liteDB)
|
||||
{
|
||||
_liteDB = liteDB;
|
||||
}
|
||||
public List<APIKey> GetAPIKeyRecordsByUserId(int userId)
|
||||
{
|
||||
var db = _liteDB.GetLiteDB();
|
||||
var table = db.GetCollection<APIKey>(tableName);
|
||||
var apiKeyRecords = table.Find(Query.EQ(nameof(APIKey.UserId), userId));
|
||||
return apiKeyRecords.ToList() ?? new List<APIKey>();
|
||||
}
|
||||
public APIKey GetAPIKeyById(int apiKeyId)
|
||||
{
|
||||
var db = _liteDB.GetLiteDB();
|
||||
var table = db.GetCollection<APIKey>(tableName);
|
||||
var apiKeyRecord = table.FindOne(Query.EQ(nameof(APIKey.Id), apiKeyId));
|
||||
return apiKeyRecord ?? new APIKey();
|
||||
}
|
||||
public APIKey GetAPIKeyByKey(string hashedKey)
|
||||
{
|
||||
var db = _liteDB.GetLiteDB();
|
||||
var table = db.GetCollection<APIKey>(tableName);
|
||||
var apiKeyRecord = table.FindOne(Query.EQ(nameof(APIKey.Key), hashedKey));
|
||||
return apiKeyRecord ?? new APIKey();
|
||||
}
|
||||
public bool SaveAPIKey(APIKey apiKey)
|
||||
{
|
||||
var db = _liteDB.GetLiteDB();
|
||||
var table = db.GetCollection<APIKey>(tableName);
|
||||
table.Upsert(apiKey);
|
||||
db.Checkpoint();
|
||||
return true;
|
||||
}
|
||||
public bool DeleteAPIKeyById(int apiKeyId)
|
||||
{
|
||||
var db = _liteDB.GetLiteDB();
|
||||
var table = db.GetCollection<APIKey>(tableName);
|
||||
table.Delete(apiKeyId);
|
||||
db.Checkpoint();
|
||||
return true;
|
||||
}
|
||||
public bool DeleteAllAPIKeysByUserId(int userId)
|
||||
{
|
||||
var db = _liteDB.GetLiteDB();
|
||||
var table = db.GetCollection<APIKey>(tableName);
|
||||
var apiKeyRecords = table.DeleteMany(Query.EQ(nameof(APIKey.UserId), userId));
|
||||
db.Checkpoint();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
186
External/Implementations/Postgres/ApiKeyRecordDataAccess.cs
vendored
Normal file
186
External/Implementations/Postgres/ApiKeyRecordDataAccess.cs
vendored
Normal file
@ -0,0 +1,186 @@
|
||||
using CarCareTracker.External.Interfaces;
|
||||
using CarCareTracker.Models;
|
||||
using Npgsql;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace CarCareTracker.External.Implementations
|
||||
{
|
||||
public class PGApiKeyRecordDataAccess : IApiKeyRecordDataAccess
|
||||
{
|
||||
private NpgsqlDataSource pgDataSource;
|
||||
private readonly ILogger<PGApiKeyRecordDataAccess> _logger;
|
||||
private static string tableName = "apikeyrecords";
|
||||
public PGApiKeyRecordDataAccess(IConfiguration config, ILogger<PGApiKeyRecordDataAccess> logger)
|
||||
{
|
||||
pgDataSource = NpgsqlDataSource.Create(config["POSTGRES_CONNECTION"]);
|
||||
|
||||
_logger = logger;
|
||||
try
|
||||
{
|
||||
//create table if not exist.
|
||||
string initCMD = $"CREATE SCHEMA IF NOT EXISTS app; CREATE TABLE IF NOT EXISTS app.{tableName} (id INT GENERATED BY DEFAULT AS IDENTITY primary key, userId INT not null, apiKey TEXT not null, data jsonb not null)";
|
||||
using (var ctext = pgDataSource.CreateCommand(initCMD))
|
||||
{
|
||||
ctext.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex.Message);
|
||||
}
|
||||
}
|
||||
public List<APIKey> GetAPIKeyRecordsByUserId(int userId)
|
||||
{
|
||||
try
|
||||
{
|
||||
string cmd = $"SELECT data FROM app.{tableName} WHERE userId = @userId";
|
||||
var results = new List<APIKey>();
|
||||
using (var ctext = pgDataSource.CreateCommand(cmd))
|
||||
{
|
||||
ctext.Parameters.AddWithValue("userId", userId);
|
||||
using (NpgsqlDataReader reader = ctext.ExecuteReader())
|
||||
while (reader.Read())
|
||||
{
|
||||
APIKey apiKeyRecord = JsonSerializer.Deserialize<APIKey>(reader["data"] as string);
|
||||
results.Add(apiKeyRecord);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex.Message);
|
||||
return new List<APIKey>();
|
||||
}
|
||||
}
|
||||
public APIKey GetAPIKeyById(int apiKeyId)
|
||||
{
|
||||
try
|
||||
{
|
||||
string cmd = $"SELECT data FROM app.{tableName} WHERE id = @id";
|
||||
var result = new APIKey();
|
||||
using (var ctext = pgDataSource.CreateCommand(cmd))
|
||||
{
|
||||
ctext.Parameters.AddWithValue("id", apiKeyId);
|
||||
using (NpgsqlDataReader reader = ctext.ExecuteReader())
|
||||
while (reader.Read())
|
||||
{
|
||||
APIKey apiKeyRecord = JsonSerializer.Deserialize<APIKey>(reader["data"] as string);
|
||||
result = apiKeyRecord;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex.Message);
|
||||
return new APIKey();
|
||||
}
|
||||
}
|
||||
public APIKey GetAPIKeyByKey(string hashedKey)
|
||||
{
|
||||
try
|
||||
{
|
||||
string cmd = $"SELECT data FROM app.{tableName} WHERE apiKey = @apiKey";
|
||||
var result = new APIKey();
|
||||
using (var ctext = pgDataSource.CreateCommand(cmd))
|
||||
{
|
||||
ctext.Parameters.AddWithValue("apiKey", hashedKey);
|
||||
using (NpgsqlDataReader reader = ctext.ExecuteReader())
|
||||
while (reader.Read())
|
||||
{
|
||||
APIKey apiKeyRecord = JsonSerializer.Deserialize<APIKey>(reader["data"] as string);
|
||||
result = apiKeyRecord;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex.Message);
|
||||
return new APIKey();
|
||||
}
|
||||
}
|
||||
public bool DeleteAPIKeyById(int apiKeyId)
|
||||
{
|
||||
try
|
||||
{
|
||||
string cmd = $"DELETE FROM app.{tableName} WHERE id = @id";
|
||||
using (var ctext = pgDataSource.CreateCommand(cmd))
|
||||
{
|
||||
ctext.Parameters.AddWithValue("id", apiKeyId);
|
||||
return ctext.ExecuteNonQuery() > 0;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex.Message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
public bool SaveAPIKey(APIKey apiKey)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (apiKey.Id == default)
|
||||
{
|
||||
string cmd = $"INSERT INTO app.{tableName} (userId, apiKey, data) VALUES(@userId, @apiKey, CAST(@data AS jsonb)) RETURNING id";
|
||||
using (var ctext = pgDataSource.CreateCommand(cmd))
|
||||
{
|
||||
ctext.Parameters.AddWithValue("userId", apiKey.UserId);
|
||||
ctext.Parameters.AddWithValue("apiKey", apiKey.Key);
|
||||
ctext.Parameters.AddWithValue("data", "{}");
|
||||
apiKey.Id = Convert.ToInt32(ctext.ExecuteScalar());
|
||||
//update json data
|
||||
if (apiKey.Id != default)
|
||||
{
|
||||
string cmdU = $"UPDATE app.{tableName} SET data = CAST(@data AS jsonb) WHERE id = @id";
|
||||
using (var ctextU = pgDataSource.CreateCommand(cmdU))
|
||||
{
|
||||
var serializedData = JsonSerializer.Serialize(apiKey);
|
||||
ctextU.Parameters.AddWithValue("id", apiKey.Id);
|
||||
ctextU.Parameters.AddWithValue("data", serializedData);
|
||||
return ctextU.ExecuteNonQuery() > 0;
|
||||
}
|
||||
}
|
||||
return apiKey.Id != default;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
string cmd = $"UPDATE app.{tableName} SET data = CAST(@data AS jsonb) WHERE id = @id";
|
||||
using (var ctext = pgDataSource.CreateCommand(cmd))
|
||||
{
|
||||
var serializedData = JsonSerializer.Serialize(apiKey);
|
||||
ctext.Parameters.AddWithValue("id", apiKey.Id);
|
||||
ctext.Parameters.AddWithValue("data", serializedData);
|
||||
return ctext.ExecuteNonQuery() > 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex.Message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
public bool DeleteAllAPIKeysByUserId(int userId)
|
||||
{
|
||||
try
|
||||
{
|
||||
string cmd = $"DELETE FROM app.{tableName} WHERE userId = @id";
|
||||
using (var ctext = pgDataSource.CreateCommand(cmd))
|
||||
{
|
||||
ctext.Parameters.AddWithValue("id", userId);
|
||||
ctext.ExecuteNonQuery();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex.Message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -7,7 +7,8 @@ namespace CarCareTracker.External.Interfaces
|
||||
public List<APIKey> GetAPIKeyRecordsByUserId(int userId);
|
||||
public APIKey GetAPIKeyById(int apiKeyId);
|
||||
public APIKey GetAPIKeyByKey(string hashedKey);
|
||||
public bool CreateNewAPIKey(APIKey apiKey);
|
||||
public bool DeleteAPIKey(int apiKeyId);
|
||||
public bool SaveAPIKey(APIKey apiKey);
|
||||
public bool DeleteAPIKeyById(int apiKeyId);
|
||||
public bool DeleteAllAPIKeysByUserId(int userId);
|
||||
}
|
||||
}
|
||||
@ -992,5 +992,20 @@ namespace CarCareTracker.Helper
|
||||
return "primary";
|
||||
}
|
||||
}
|
||||
public static string GetHash(string value)
|
||||
{
|
||||
StringBuilder Sb = new StringBuilder();
|
||||
|
||||
using (var hash = SHA256.Create())
|
||||
{
|
||||
Encoding enc = Encoding.UTF8;
|
||||
byte[] result = hash.ComputeHash(enc.GetBytes(value));
|
||||
|
||||
foreach (byte b in result)
|
||||
Sb.Append(b.ToString("x2"));
|
||||
}
|
||||
|
||||
return Sb.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -103,7 +103,7 @@ namespace CarCareTracker.Logic
|
||||
if (!string.IsNullOrWhiteSpace(credentials.Password))
|
||||
{
|
||||
//update password
|
||||
existingUser.Password = GetHash(credentials.Password);
|
||||
existingUser.Password = StaticHelper.GetHash(credentials.Password);
|
||||
}
|
||||
//delete token
|
||||
_tokenData.DeleteToken(existingToken.Id);
|
||||
@ -136,7 +136,7 @@ namespace CarCareTracker.Logic
|
||||
var newUser = new UserData()
|
||||
{
|
||||
UserName = credentials.UserName,
|
||||
Password = GetHash(NewToken()), //generate a password for OpenID User
|
||||
Password = StaticHelper.GetHash(NewToken()), //generate a password for OpenID User
|
||||
EmailAddress = credentials.EmailAddress
|
||||
};
|
||||
var result = _userData.SaveUserRecord(newUser);
|
||||
@ -178,7 +178,7 @@ namespace CarCareTracker.Logic
|
||||
var newUser = new UserData()
|
||||
{
|
||||
UserName = credentials.UserName,
|
||||
Password = GetHash(credentials.Password),
|
||||
Password = StaticHelper.GetHash(credentials.Password),
|
||||
EmailAddress = credentials.EmailAddress
|
||||
};
|
||||
var result = _userData.SaveUserRecord(newUser);
|
||||
@ -235,7 +235,7 @@ namespace CarCareTracker.Logic
|
||||
{
|
||||
return OperationResponse.Failed("Unable to locate user");
|
||||
}
|
||||
existingUser.Password = GetHash(credentials.Password);
|
||||
existingUser.Password = StaticHelper.GetHash(credentials.Password);
|
||||
var result = _userData.SaveUserRecord(existingUser);
|
||||
//delete token
|
||||
_tokenData.DeleteToken(existingToken.Id);
|
||||
@ -262,7 +262,7 @@ namespace CarCareTracker.Logic
|
||||
{
|
||||
//authenticate via DB.
|
||||
var result = _userData.GetUserRecordByUserName(credentials.UserName);
|
||||
if (GetHash(credentials.Password) == result.Password)
|
||||
if (StaticHelper.GetHash(credentials.Password) == result.Password)
|
||||
{
|
||||
result.Password = string.Empty;
|
||||
return result;
|
||||
@ -376,7 +376,7 @@ namespace CarCareTracker.Logic
|
||||
return OperationResponse.Failed("Unable to find user");
|
||||
}
|
||||
var newPassword = Guid.NewGuid().ToString().Substring(0, 8);
|
||||
existingUser.Password = GetHash(newPassword);
|
||||
existingUser.Password = StaticHelper.GetHash(newPassword);
|
||||
var result = _userData.SaveUserRecord(existingUser);
|
||||
if (result)
|
||||
{
|
||||
@ -399,8 +399,8 @@ namespace CarCareTracker.Logic
|
||||
if (existingUserConfig is not null)
|
||||
{
|
||||
//create hashes of the login credentials.
|
||||
var hashedUserName = GetHash(credentials.UserName);
|
||||
var hashedPassword = GetHash(credentials.Password);
|
||||
var hashedUserName = StaticHelper.GetHash(credentials.UserName);
|
||||
var hashedPassword = StaticHelper.GetHash(credentials.Password);
|
||||
//copy over settings that are off limits on the settings page.
|
||||
existingUserConfig.EnableAuth = true;
|
||||
existingUserConfig.UserNameHash = hashedUserName;
|
||||
@ -412,8 +412,8 @@ namespace CarCareTracker.Logic
|
||||
var newUserConfig = new UserConfig()
|
||||
{
|
||||
EnableAuth = true,
|
||||
UserNameHash = GetHash(credentials.UserName),
|
||||
UserPasswordHash = GetHash(credentials.Password)
|
||||
UserNameHash = StaticHelper.GetHash(credentials.UserName),
|
||||
UserPasswordHash = StaticHelper.GetHash(credentials.Password)
|
||||
};
|
||||
File.WriteAllText(StaticHelper.UserConfigPath, JsonSerializer.Serialize(newUserConfig));
|
||||
}
|
||||
@ -438,8 +438,8 @@ namespace CarCareTracker.Logic
|
||||
}
|
||||
private bool UserIsRoot(LoginModel credentials)
|
||||
{
|
||||
var hashedUserName = GetHash(credentials.UserName);
|
||||
var hashedPassword = GetHash(credentials.Password);
|
||||
var hashedUserName = StaticHelper.GetHash(credentials.UserName);
|
||||
var hashedPassword = StaticHelper.GetHash(credentials.Password);
|
||||
return _configHelper.AuthenticateRootUser(hashedUserName, hashedPassword);
|
||||
}
|
||||
private UserData GetRootUserData(string username)
|
||||
@ -454,21 +454,7 @@ namespace CarCareTracker.Logic
|
||||
};
|
||||
}
|
||||
#endregion
|
||||
private static string GetHash(string value)
|
||||
{
|
||||
StringBuilder Sb = new StringBuilder();
|
||||
|
||||
using (var hash = SHA256.Create())
|
||||
{
|
||||
Encoding enc = Encoding.UTF8;
|
||||
byte[] result = hash.ComputeHash(enc.GetBytes(value));
|
||||
|
||||
foreach (byte b in result)
|
||||
Sb.Append(b.ToString("x2"));
|
||||
}
|
||||
|
||||
return Sb.ToString();
|
||||
}
|
||||
|
||||
private string NewToken()
|
||||
{
|
||||
return Guid.NewGuid().ToString().Substring(0, 8);
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
using CarCareTracker.External.Interfaces;
|
||||
using CarCareTracker.Helper;
|
||||
using CarCareTracker.Models;
|
||||
|
||||
namespace CarCareTracker.Logic
|
||||
@ -22,18 +23,24 @@ namespace CarCareTracker.Logic
|
||||
bool DeleteUserFromHousehold(int parentUserId, int childUserId);
|
||||
bool DeleteAllHouseholdByParentUserId(int parentUserId);
|
||||
bool DeleteAllHouseholdByChildUserId(int childUserId);
|
||||
OperationResponse CreateAPIKey(int userId, string keyName, List<HouseholdPermission> permisions);
|
||||
bool DeleteAPIKeyByKeyId(int keyId);
|
||||
bool DeleteAllAPIKeysByUserId(int userId);
|
||||
}
|
||||
public class UserLogic: IUserLogic
|
||||
{
|
||||
private readonly IUserAccessDataAccess _userAccess;
|
||||
private readonly IUserRecordDataAccess _userData;
|
||||
private readonly IUserHouseholdDataAccess _userHouseholdData;
|
||||
private readonly IApiKeyRecordDataAccess _apiKeyData;
|
||||
public UserLogic(IUserAccessDataAccess userAccess,
|
||||
IUserRecordDataAccess userData,
|
||||
IUserHouseholdDataAccess userHouseholdData) {
|
||||
IUserHouseholdDataAccess userHouseholdData,
|
||||
IApiKeyRecordDataAccess apiKeyData) {
|
||||
_userAccess = userAccess;
|
||||
_userData = userData;
|
||||
_userHouseholdData = userHouseholdData;
|
||||
_apiKeyData = apiKeyData;
|
||||
}
|
||||
public List<UserCollaborator> GetCollaboratorsForVehicle(int vehicleId)
|
||||
{
|
||||
@ -287,5 +294,33 @@ namespace CarCareTracker.Logic
|
||||
var result = _userHouseholdData.DeleteAllHouseholdRecordsByChildUserId(childUserId);
|
||||
return result;
|
||||
}
|
||||
public OperationResponse CreateAPIKey(int userId, string keyName, List<HouseholdPermission> permisions)
|
||||
{
|
||||
//generate key pair
|
||||
var unhashedKey = Guid.NewGuid().ToString().Replace("-", string.Empty);
|
||||
var hashedKey = StaticHelper.GetHash(unhashedKey);
|
||||
var keyToSave = new APIKey
|
||||
{
|
||||
UserId = userId,
|
||||
Name = keyName,
|
||||
Permissions = permisions
|
||||
};
|
||||
var result = _apiKeyData.SaveAPIKey(keyToSave);
|
||||
if (result && keyToSave.Id != default)
|
||||
{
|
||||
return OperationResponse.Succeed("API Key Created", new {apiKey = unhashedKey});
|
||||
}
|
||||
return OperationResponse.Failed("Unable to create API Key");
|
||||
}
|
||||
public bool DeleteAPIKeyByKeyId(int keyId)
|
||||
{
|
||||
var result = _apiKeyData.DeleteAPIKeyById(keyId);
|
||||
return result;
|
||||
}
|
||||
public bool DeleteAllAPIKeysByUserId(int userId)
|
||||
{
|
||||
var result = _apiKeyData.DeleteAllAPIKeysByUserId(userId);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -62,6 +62,7 @@ if (!string.IsNullOrWhiteSpace(builder.Configuration["POSTGRES_CONNECTION"])){
|
||||
builder.Services.AddSingleton<IInspectionRecordTemplateDataAccess, PGInspectionRecordTemplateDataAccess>();
|
||||
builder.Services.AddSingleton<IEquipmentRecordDataAccess, PGEquipmentRecordDataAccess>();
|
||||
builder.Services.AddSingleton<IUserHouseholdDataAccess, PGUserHouseholdDataAccess>();
|
||||
builder.Services.AddSingleton<IApiKeyRecordDataAccess, PGApiKeyRecordDataAccess>();
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -86,6 +87,7 @@ else
|
||||
builder.Services.AddSingleton<IInspectionRecordTemplateDataAccess, InspectionRecordTemplateDataAccess>();
|
||||
builder.Services.AddSingleton<IEquipmentRecordDataAccess, EquipmentRecordDataAccess>();
|
||||
builder.Services.AddSingleton<IUserHouseholdDataAccess, UserHouseholdDataAccess>();
|
||||
builder.Services.AddSingleton<IApiKeyRecordDataAccess, ApiKeyRecordDataAccess>();
|
||||
}
|
||||
|
||||
//configure helpers
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user