aboutsummaryrefslogtreecommitdiff
path: root/Libs/VNLib.Plugins.Essentials.Oauth/src/Applications/ApplicationStore.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Libs/VNLib.Plugins.Essentials.Oauth/src/Applications/ApplicationStore.cs')
-rw-r--r--Libs/VNLib.Plugins.Essentials.Oauth/src/Applications/ApplicationStore.cs229
1 files changed, 117 insertions, 112 deletions
diff --git a/Libs/VNLib.Plugins.Essentials.Oauth/src/Applications/ApplicationStore.cs b/Libs/VNLib.Plugins.Essentials.Oauth/src/Applications/ApplicationStore.cs
index da70a17..17db978 100644
--- a/Libs/VNLib.Plugins.Essentials.Oauth/src/Applications/ApplicationStore.cs
+++ b/Libs/VNLib.Plugins.Essentials.Oauth/src/Applications/ApplicationStore.cs
@@ -27,7 +27,6 @@ using System.Data;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-using System.Collections.Generic;
using Microsoft.EntityFrameworkCore;
@@ -37,6 +36,8 @@ using VNLib.Utils.Memory;
using VNLib.Plugins.Extensions.Data;
using VNLib.Plugins.Essentials.Accounts;
using VNLib.Plugins.Essentials.Oauth.Tokens;
+using VNLib.Plugins.Extensions.Data.Abstractions;
+using VNLib.Plugins.Extensions.Data.Extensions;
namespace VNLib.Plugins.Essentials.Oauth.Applications
{
@@ -52,6 +53,7 @@ namespace VNLib.Plugins.Essentials.Oauth.Applications
private readonly DbContextOptions ConextOptions;
private readonly ITokenManager TokenStore;
+
/// <summary>
/// Initializes a new <see cref="ApplicationStore"/> data store
/// uisng the specified EFCore <see cref="DbContextOptions"/> object.
@@ -64,8 +66,27 @@ namespace VNLib.Plugins.Essentials.Oauth.Applications
SecretHashing = secretHashing;
TokenStore = new TokenStore(conextOptions);
}
-
-
+
+
+ /// <summary>
+ /// Generates a client application secret using the <see cref="RandomHash"/> library
+ /// </summary>
+ /// <returns>The RNG secret</returns>
+ public static PrivateString GenerateSecret(int secretSize = SECRET_SIZE) => (PrivateString)RandomHash.GetRandomHex(secretSize).ToLower(null)!;
+
+ /// <inheritdoc/>
+ public override IDbContextHandle GetNewContext() => new UserAppContext(ConextOptions);
+
+ /// <inheritdoc/>
+ public override string GetNewRecordId() => RandomHash.GetRandomHex(CLIENT_ID_SIZE).ToLower(null);
+
+ ///<inheritdoc/>
+ public override void OnRecordUpdate(UserApplication newRecord, UserApplication currentRecord)
+ {
+ currentRecord.AppDescription = newRecord.AppDescription;
+ currentRecord.AppName = newRecord.AppName;
+ }
+
/// <summary>
/// Updates the secret of an application, and if successful returns the new raw secret data
/// </summary>
@@ -123,6 +144,7 @@ namespace VNLib.Plugins.Essentials.Oauth.Applications
public async Task<UserApplication?> VerifyAppAsync(string clientId, PrivateString secret)
{
UserApplication? app;
+
//Open new db context
await using (UserAppContext Database = new(ConextOptions))
{
@@ -136,14 +158,17 @@ namespace VNLib.Plugins.Essentials.Oauth.Applications
//commit the transaction
await Database.CommitTransactionAsync();
}
+
//make sure app exists
if (string.IsNullOrWhiteSpace(app?.UserId) || !app.ClientId!.Equals(clientId, StringComparison.Ordinal))
{
//Not found or not valid
return null;
}
+
//Convert the secret hash to a private string so it will be cleaned up
using PrivateString secretHash = (PrivateString)app.SecretHash!;
+
//Verify the secret against the hash
if (SecretHashing.Verify(secretHash, secret))
{
@@ -151,134 +176,114 @@ namespace VNLib.Plugins.Essentials.Oauth.Applications
//App was successfully verified
return app;
}
+
//Not found or not valid
return null;
}
- ///<inheritdoc/>
- public override async Task<ERRNO> DeleteAsync(params string[] specifiers)
- {
- //get app id to remove tokens from
- string appId = specifiers[0];
- //Delete app from store
- ERRNO result = await base.DeleteAsync(specifiers);
- if(result)
- {
- //Delete active tokens
- await TokenStore.RevokeTokensForAppAsync(appId, CancellationToken.None);
- }
- return result;
- }
/// <summary>
- /// Generates a client application secret using the <see cref="RandomHash"/> library
- /// </summary>
- /// <returns>The RNG secret</returns>
- public static PrivateString GenerateSecret() => (PrivateString)RandomHash.GetRandomHex(SECRET_SIZE).ToLower()!;
- /// <summary>
/// Creates and initializes a new <see cref="UserApplication"/> with a random clientid and
/// secret that must be disposed
/// </summary>
/// <param name="record">The new record to create</param>
+ /// <param name="cancellation"></param>
/// <returns>The result of the operation</returns>
- public override async Task<ERRNO> CreateAsync(UserApplication record, CancellationToken cancellation = default)
+ public async Task<ERRNO> CreateAppAsync(UserApplication record, CancellationToken cancellation = default)
{
record.RawSecret = GenerateSecret();
//Hash the secret
using PrivateString secretHash = SecretHashing.Hash(record.RawSecret);
- record.ClientId = GenerateClientID();
+ record.ClientId = GetNewRecordId();
record.SecretHash = (string)secretHash;
- //Wait for the rescord to be created before wiping the secret
- return await base.CreateAsync(record, cancellation);
+ //Wait for the record to be created before wiping the secret
+ return await this.CreateAsync(record, cancellation);
}
+
- /// <summary>
- /// Generates a new client ID using the <see cref="RandomHash"/> library
- /// </summary>
- /// <returns>The new client ID</returns>
- public static string GenerateClientID() => RandomHash.GetRandomHex(CLIENT_ID_SIZE).ToLower();
- ///<inheritdoc/>
- public override string RecordIdBuilder => RandomHash.GetRandomHex(CLIENT_ID_SIZE).ToLower();
- ///<inheritdoc/>
- public override TransactionalDbContext NewContext() => new UserAppContext(ConextOptions);
- ///<inheritdoc/>
- protected override IQueryable<UserApplication> AddOrUpdateQueryBuilder(TransactionalDbContext context, UserApplication record)
- {
- UserAppContext ctx = (context as UserAppContext)!;
- //get a single record by the id for the specific user
- return from userApp in ctx.OAuthApps
- where userApp.UserId == record.UserId
- && userApp.Id == record.Id
- select userApp;
- }
- ///<inheritdoc/>
- protected override void OnRecordUpdate(UserApplication newRecord, UserApplication currentRecord)
- {
- currentRecord.AppDescription = newRecord.AppDescription;
- currentRecord.AppName = newRecord.AppName;
- }
- ///<inheritdoc/>
- protected override IQueryable<UserApplication> GetCollectionQueryBuilder(TransactionalDbContext context, string specifier)
- {
- UserAppContext ctx = (context as UserAppContext)!;
- //Get the user's applications based on their userid
- return from userApp in ctx.OAuthApps
- where userApp.UserId == specifier
- orderby userApp.Created ascending
- select new UserApplication
- {
- AppDescription = userApp.AppDescription,
- Id = userApp.Id,
- AppName = userApp.AppName,
- ClientId = userApp.ClientId,
- Created = userApp.Created,
- Permissions = userApp.Permissions
- };
- }
- ///<inheritdoc/>
- protected override IQueryable<UserApplication> GetCollectionQueryBuilder(TransactionalDbContext context, params string[] constraints)
- {
- return GetCollectionQueryBuilder(context, constraints[0]);
- }
- ///<inheritdoc/>
- protected override IQueryable<UserApplication> GetSingleQueryBuilder(TransactionalDbContext context, params string[] constraints)
- {
- string appId = constraints[0];
- string userId = constraints[1];
- UserAppContext ctx = (context as UserAppContext)!;
- //Query to get a new single application with limit results output
- return from userApp in ctx.OAuthApps
- where userApp.UserId == userId
- && userApp.Id == appId
- select new UserApplication
- {
- AppDescription = userApp.AppDescription,
- Id = userApp.Id,
- AppName = userApp.AppName,
- ClientId = userApp.ClientId,
- Created = userApp.Created,
- Permissions = userApp.Permissions,
- Version = userApp.Version
- };
- }
- ///<inheritdoc/>
- protected override IQueryable<UserApplication> GetSingleQueryBuilder(TransactionalDbContext context, UserApplication record)
- {
- //Use the query for single record with the other record's userid and record id
- return GetSingleQueryBuilder(context, record.Id, record.UserId);
- }
- ///<inheritdoc/>
- protected override IQueryable<UserApplication> UpdateQueryBuilder(TransactionalDbContext context, UserApplication record)
- {
- UserAppContext ctx = (context as UserAppContext)!;
- return from userApp in ctx.OAuthApps
- where userApp.UserId == record.UserId && userApp.Id == record.Id
- select userApp;
- }
+ public override IDbQueryLookup<UserApplication> QueryTable { get; } = new ApplicationQueries();
- //DO NOT ALLOW PAGINATION YET
- public override Task<int> GetPageAsync(ICollection<UserApplication> collection, int page, int limit, CancellationToken cancellation = default)
+ sealed class ApplicationQueries : IDbQueryLookup<UserApplication>
{
- throw new NotSupportedException();
+ ///<inheritdoc/>
+ public IQueryable<UserApplication> GetCollectionQueryBuilder(IDbContextHandle context, params string[] constraints)
+ {
+ //When only a single contraint is specified, we are getting all applications for a user
+ if (constraints.Length == 1)
+ {
+ string userId = constraints[0];
+
+ UserAppContext ctx = (context as UserAppContext)!;
+ //Get the user's applications based on their userid
+ return from userApp in ctx.OAuthApps
+ where userApp.UserId == userId
+ orderby userApp.Created ascending
+ select new UserApplication
+ {
+ AppDescription = userApp.AppDescription,
+ Id = userApp.Id,
+ AppName = userApp.AppName,
+ ClientId = userApp.ClientId,
+ Created = userApp.Created,
+ Permissions = userApp.Permissions
+ };
+ }
+ //When two constraints are specified, we are getting a single application
+ else
+ {
+ string appId = constraints[0];
+ string userId = constraints[1];
+
+ UserAppContext ctx = (context as UserAppContext)!;
+ //Query to get a new single application with limit results output
+ return from userApp in ctx.OAuthApps
+ where userApp.UserId == userId
+ && userApp.Id == appId
+ select new UserApplication
+ {
+ AppDescription = userApp.AppDescription,
+ Id = userApp.Id,
+ AppName = userApp.AppName,
+ ClientId = userApp.ClientId,
+ Created = userApp.Created,
+ Permissions = userApp.Permissions,
+ Version = userApp.Version
+ };
+ }
+ }
+
+ ///<inheritdoc/>
+ public IQueryable<UserApplication> GetSingleQueryBuilder(IDbContextHandle context, params string[] constraints)
+ {
+ string appId = constraints[0];
+ string userId = constraints[1];
+ UserAppContext ctx = (context as UserAppContext)!;
+ //Query to get a new single application with limit results output
+ return from userApp in ctx.OAuthApps
+ where userApp.UserId == userId
+ && userApp.Id == appId
+ select new UserApplication
+ {
+ AppDescription = userApp.AppDescription,
+ Id = userApp.Id,
+ AppName = userApp.AppName,
+ ClientId = userApp.ClientId,
+ Created = userApp.Created,
+ Permissions = userApp.Permissions,
+ Version = userApp.Version
+ };
+ }
+
+ ///<inheritdoc/>
+ public IQueryable<UserApplication> AddOrUpdateQueryBuilder(IDbContextHandle context, UserApplication record)
+ {
+ UserAppContext ctx = (context as UserAppContext)!;
+ //get a single record by the id for the specific user
+ return from userApp in ctx.OAuthApps
+ where userApp.UserId == record.UserId
+ && userApp.Id == record.Id
+ select userApp;
+ }
+
}
}
} \ No newline at end of file