341 lines
10 KiB
C#
341 lines
10 KiB
C#
using System;
|
|
using System.Linq;
|
|
using xIds.Helpers;
|
|
using System.Threading;
|
|
using xCommons.Extensions;
|
|
using xIdentityModels.Dtos;
|
|
using xExceptions.Constants;
|
|
using System.Threading.Tasks;
|
|
using xIdentityModels.Models;
|
|
using System.Collections.Generic;
|
|
using System.Security.Cryptography;
|
|
|
|
namespace xIds.Providers
|
|
{
|
|
public partial class XApplicationProvider
|
|
{
|
|
//
|
|
#region Custom Actions ...
|
|
/// <summary>
|
|
/// Generate a New ApiKey and Add It to DB ...
|
|
/// </summary>
|
|
/// <param name="applicationId"></param>
|
|
/// <param name="expiration"></param>
|
|
/// <param name="scopes"></param>
|
|
/// <param name="allowedIPs"></param>
|
|
/// <param name="rateLimit"></param>
|
|
/// <param name="userInfo"></param>
|
|
/// <param name="cancellationToken"></param>
|
|
/// <returns></returns>
|
|
public async Task<XApiKeyDto> CreateApiKey(
|
|
int applicationId,
|
|
TimeSpan? expiration = null,
|
|
IEnumerable<string> scopes = null,
|
|
IEnumerable<string> allowedIPs = null,
|
|
int? rateLimit = null,
|
|
XUserClaimsInfoDto userInfo = null,
|
|
CancellationToken cancellationToken = default
|
|
)
|
|
{
|
|
//
|
|
// Validate ...
|
|
if (
|
|
userInfo.IsNullOrDefault() ||
|
|
!applicationId.IsValidIntId()
|
|
)
|
|
{
|
|
XException.InvalidArgs.Throw();
|
|
}
|
|
|
|
//
|
|
// Check Application Exists ...
|
|
var application = await GetApplicationAsync(
|
|
id: applicationId,
|
|
userInfo: userInfo,
|
|
includeBuilder: null,
|
|
ignoreSoftDeleteds: true,
|
|
cancellationToken: cancellationToken
|
|
);
|
|
if (application.IsNullOrDefault())
|
|
{
|
|
XException.NotFound.Throw();
|
|
}
|
|
|
|
//
|
|
// Validate Owner ...
|
|
if (!IsOwned(application, userInfo))
|
|
{
|
|
XException.NotAllowed.Throw();
|
|
}
|
|
|
|
//
|
|
// Normalizing ...
|
|
if (!rateLimit.HasValue)
|
|
{
|
|
rateLimit = apiKeyConfiguration.DefaultRateLimit;
|
|
}
|
|
if (!expiration.HasValue)
|
|
{
|
|
expiration = TimeSpan.FromMinutes(apiKeyConfiguration.DefaultExpirationMinutes);
|
|
}
|
|
else if (expiration.Value > TimeSpan.FromMinutes(apiKeyConfiguration.MaxExpirationMinutes))
|
|
{
|
|
expiration = TimeSpan.FromMinutes(apiKeyConfiguration.MaxExpirationMinutes);
|
|
}
|
|
|
|
//
|
|
// Generate New Key ...
|
|
(string plainKey, string keyHash, string prefix) = XApiKeyHelper.Generate();
|
|
var expireAt = DateTime.UtcNow.Add(expiration.Value);
|
|
|
|
//
|
|
var _allowedScopes = scopes.ToJSON();
|
|
var _allowedIPs = allowedIPs.ToListString();
|
|
|
|
//
|
|
// Instance Dto Item ...
|
|
var result = new XApiKeyDto
|
|
{
|
|
RevokedAt = null,
|
|
KeyHash = keyHash,
|
|
LastUsedAt = null,
|
|
KeyPrefix = prefix,
|
|
ExpiresAt = expireAt,
|
|
RevokedBy = string.Empty,
|
|
AllowedIPs = _allowedIPs,
|
|
CreatedOn = DateTime.UtcNow,
|
|
ApplicationId = applicationId,
|
|
AllowedScopes = _allowedScopes,
|
|
RateLimitPerMinute = rateLimit.Value
|
|
};
|
|
|
|
//
|
|
// Add Item to Database ...
|
|
result = await AddApiKeyAsync(
|
|
item: item,
|
|
userInfo: userInfo,
|
|
cancellationToken: cancellationToken
|
|
);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Revoke Specified Api Key ...
|
|
/// </summary>
|
|
/// <param name="apiKeyId"></param>
|
|
/// <param name="userInfo"></param>
|
|
/// <param name="cancellationToken"></param>
|
|
/// <returns></returns>
|
|
public async Task<bool> RevokeApiKey(
|
|
Guid apiKeyId,
|
|
XUserClaimsInfoDto userInfo = null,
|
|
CancellationToken cancellationToken = default
|
|
)
|
|
{
|
|
//
|
|
// Validate ...
|
|
if (
|
|
apiKeyId.IsDefaultGuid() ||
|
|
userInfo.IsNullOrDefault())
|
|
{
|
|
XException.InvalidArgs.Throw();
|
|
}
|
|
|
|
//
|
|
// Retrieve ApiKey ...
|
|
var item = await GetApiKeyAsync(
|
|
id: apiKeyId,
|
|
userInfo: userInfo,
|
|
includeBuilder: null,
|
|
ignoreSoftDeleteds: true,
|
|
cancellationToken: cancellationToken
|
|
);
|
|
if (item.IsNullOrDefault())
|
|
{
|
|
XException.NotFound.Throw();
|
|
}
|
|
|
|
//
|
|
// Permission Checking ...
|
|
var application = await GetApplicationAsync(
|
|
userInfo: userInfo,
|
|
includeBuilder: null,
|
|
id: item.ApplicationId,
|
|
ignoreSoftDeleteds: true,
|
|
cancellationToken: cancellationToken
|
|
);
|
|
if (application.IsNullOrDefault())
|
|
{
|
|
XException.NotFound.Throw();
|
|
}
|
|
if (!IsOwned(application, userInfo))
|
|
{
|
|
XException.NotAllowed.Throw();
|
|
}
|
|
|
|
//
|
|
// Update Revoke Info ...
|
|
item.RevokedAt = DateTime.UtcNow;
|
|
item.RevokedBy = userInfo.UserId;
|
|
|
|
//
|
|
item = await UpdateApiKeyAsync(
|
|
id: item.Id,
|
|
item: item,
|
|
userInfo: userInfo,
|
|
cancellationToken: cancellationToken
|
|
);
|
|
|
|
//
|
|
var result = !item.IsNullOrDefault();
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Renew Specified Api Key ...
|
|
/// </summary>
|
|
/// <param name="apiKeyId"></param>
|
|
/// <param name="newExpiration"></param>
|
|
/// <param name="userInfo"></param>
|
|
/// <param name="cancellationToken"></param>
|
|
/// <returns></returns>
|
|
public async Task<XApiKeyDto> RotateApiKey(
|
|
Guid apiKeyId,
|
|
TimeSpan? newExpiration = null,
|
|
XUserClaimsInfoDto userInfo = null,
|
|
CancellationToken cancellationToken = default
|
|
)
|
|
{
|
|
//
|
|
// Validate ...
|
|
if (
|
|
apiKeyId.IsDefaultGuid() ||
|
|
userInfo.IsNullOrDefault()
|
|
)
|
|
{
|
|
XException.InvalidArgs.Throw();
|
|
}
|
|
|
|
//
|
|
// Retrieve Item ...
|
|
var result = await GetApiKeyAsync(
|
|
id: apiKeyId,
|
|
userInfo: userInfo,
|
|
includeBuilder: null,
|
|
ignoreSoftDeleteds: true,
|
|
cancellationToken: cancellationToken
|
|
);
|
|
if (result.IsNullOrDefault())
|
|
{
|
|
XException.NotFound.Throw();
|
|
}
|
|
|
|
//
|
|
// Expired Date ...
|
|
DateTime? expiredDate = null;
|
|
if (!newExpiration.HasValue)
|
|
{
|
|
//
|
|
newExpiration = TimeSpan
|
|
.FromMinutes(apiKeyConfiguration.DefaultExpirationMinutes);
|
|
expiredDate = DateTime.UtcNow.Add(newExpiration.Value);
|
|
}
|
|
if (!expiredDate.HasValue)
|
|
{
|
|
XException.InvalidArgs.Throw();
|
|
}
|
|
|
|
//
|
|
// Update Item ...
|
|
result.ExpiresAt = expiredDate.Value;
|
|
|
|
//
|
|
result = await UpdateApiKeyAsync(
|
|
item: result,
|
|
id: result.Id,
|
|
userInfo: userInfo,
|
|
cancellationToken: cancellationToken
|
|
);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Retrieve All Api Keys for Specified Application ...
|
|
/// </summary>
|
|
/// <param name="applicationId"></param>
|
|
/// <param name="userInfo"></param>
|
|
/// <param name="cancellationToken"></param>
|
|
/// <returns></returns>
|
|
public async Task<IEnumerable<XApiKeyDto>> GetApplicationApiKeys(
|
|
int applicationId,
|
|
XUserClaimsInfoDto userInfo = null,
|
|
CancellationToken cancellationToken = default
|
|
)
|
|
{
|
|
//
|
|
// Validate ...
|
|
if (
|
|
userInfo.IsNullOrDefault() ||
|
|
!applicationId.IsValidIntId()
|
|
)
|
|
{
|
|
XException.InvalidArgs.Throw();
|
|
}
|
|
|
|
//
|
|
// Permission Checking ...
|
|
var application = await GetApplicationAsync(
|
|
userInfo: userInfo,
|
|
includeBuilder: null,
|
|
id: applicationId,
|
|
ignoreSoftDeleteds: true,
|
|
cancellationToken: cancellationToken
|
|
);
|
|
if (application.IsNullOrDefault())
|
|
{
|
|
XException.NotFound.Throw();
|
|
}
|
|
if (!IsOwned(application, userInfo))
|
|
{
|
|
XException.NotAllowed.Throw();
|
|
}
|
|
|
|
//
|
|
var result = await FindManyApiKeyAsync(
|
|
userInfo: userInfo,
|
|
includeBuilder: null,
|
|
ignoreSoftDeleteds: true,
|
|
cancellationToken: cancellationToken,
|
|
orderBuilder: x => x.OrderBy(y => y.Id),
|
|
predicate: x => x.ApplicationId == applicationId
|
|
);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Validate Specified Api Key in based on ClientIP ...
|
|
/// </summary>
|
|
/// <param name="apiKey"></param>
|
|
/// <param name="clientIP"></param>
|
|
/// <param name="userInfo"></param>
|
|
/// <param name="cancellationToken"></param>
|
|
/// <returns></returns>
|
|
public async Task<bool> ValidateApiKey(
|
|
string apiKey,
|
|
string clientIP,
|
|
XUserClaimsInfoDto userInfo = null,
|
|
CancellationToken cancellationToken = default
|
|
)
|
|
{
|
|
//
|
|
|
|
}
|
|
#endregion
|
|
}
|
|
} |