aboutsummaryrefslogtreecommitdiff
path: root/docker/crupest-api/CrupestApi/CrupestApi.Secrets/SecretsService.cs
diff options
context:
space:
mode:
Diffstat (limited to 'docker/crupest-api/CrupestApi/CrupestApi.Secrets/SecretsService.cs')
-rw-r--r--docker/crupest-api/CrupestApi/CrupestApi.Secrets/SecretsService.cs162
1 files changed, 154 insertions, 8 deletions
diff --git a/docker/crupest-api/CrupestApi/CrupestApi.Secrets/SecretsService.cs b/docker/crupest-api/CrupestApi/CrupestApi.Secrets/SecretsService.cs
index feac08a..5cdcc54 100644
--- a/docker/crupest-api/CrupestApi/CrupestApi.Secrets/SecretsService.cs
+++ b/docker/crupest-api/CrupestApi/CrupestApi.Secrets/SecretsService.cs
@@ -1,3 +1,5 @@
+using System.Data;
+using System.Diagnostics;
using System.Security.Cryptography;
using System.Text;
using CrupestApi.Commons;
@@ -86,6 +88,25 @@ INSERT INTO secrets (Key, Secret, Description, ExpireTime, Revoked, CreateTime)
return result.ToString();
}
+ private async Task<SecretInfo> GetSecretAsync(IDbConnection dbConnection, string secret)
+ {
+ var result = await dbConnection.QueryFirstOrDefaultAsync<SecretInfo>(@"
+SELECT Id, Key, Secret, Description, ExpireTime, Revoked, CreateTime FROM secrets WHERE Secret = @Secret;
+ ", new
+ {
+ Secret = secret
+ });
+
+ return result;
+
+ }
+
+ public async Task<SecretInfo?> GetSecretAsync(string secret)
+ {
+ using var dbConnection = await EnsureDatabase();
+ return await GetSecretAsync(dbConnection, secret);
+ }
+
public async Task<SecretInfo> CreateSecretAsync(string key, string description, DateTime? expireTime = null)
{
var dbConnection = await EnsureDatabase();
@@ -146,23 +167,148 @@ WHERE Key = @Key AND
return query.ToList();
}
- public Task<SecretInfo> ModifySecretAsync(string secret, SecretModifyRequest modifyRequest)
+ public async Task<SecretInfo> ModifySecretAsync(string secret, SecretModifyRequest modifyRequest)
{
- throw new NotImplementedException();
+ var dbConnection = await EnsureDatabase();
+
+ var secretInfo = await GetSecretAsync(dbConnection, secret);
+
+ if (secretInfo is null)
+ {
+ throw new EntityNotExistException("Secret not found.");
+ }
+
+ var queryParams = new DynamicParameters();
+ var updateColumnList = new List<string>();
+
+ if (modifyRequest.Key is not null)
+ {
+ queryParams.Add("Key", modifyRequest.Key);
+ updateColumnList.Add("Key");
+ }
+
+ if (modifyRequest.Description is not null)
+ {
+ queryParams.Add("Description", modifyRequest.Description);
+ updateColumnList.Add("Description");
+ }
+
+ if (modifyRequest.SetExpireTime is true)
+ {
+ queryParams.Add("ExpireTime", modifyRequest.ExpireTime?.ToString("O"));
+ updateColumnList.Add("ExpireTime");
+ }
+
+ if (modifyRequest.Revoked is true && secretInfo.Revoked is not true)
+ {
+ queryParams.Add("Revoked", true);
+ updateColumnList.Add("Revoked");
+ }
+
+ if (updateColumnList.Count == 0)
+ {
+ return secretInfo;
+ }
+
+ queryParams.Add("Secret", secret);
+
+ var updateColumnString = updateColumnList.GenerateUpdateColumnString();
+
+ var changeCount = await dbConnection.ExecuteAsync($@"
+UPDATE secrets SET {updateColumnString} WHERE Secret = @Secret;
+ ", queryParams);
+
+ Debug.Assert(changeCount == 1);
+
+ return secretInfo;
}
- public Task RevokeSecretAsync(string secret)
+ public async Task RevokeSecretAsync(string secret)
{
- throw new NotImplementedException();
+ await ModifySecretAsync(secret, new SecretModifyRequest
+ {
+ Revoked = true,
+ });
}
- public Task<bool> VerifySecretAsync(string key, string secret)
+ public async Task VerifySecretAsync(string? key, string? secret)
{
- throw new NotImplementedException();
+ var dbConnection = await EnsureDatabase();
+
+ if (secret is null)
+ {
+ if (key is not null)
+ {
+ throw new VerifySecretException(key, "A secret with given key is needed.");
+ }
+ }
+
+ var entity = await dbConnection.QueryFirstOrDefaultAsync<SecretInfo>(@"
+SELECT Id, Key, Secret, Description, ExpireTime, Revoked, CreateTime FROM secrets WHERE Key = @Key AND Secret = @Secret
+ ", new
+ {
+ Key = key,
+ Secret = secret,
+ });
+
+ if (entity is null)
+ {
+ throw new VerifySecretException(key, "Secret token is invalid.");
+ }
+
+ if (entity.Revoked is true)
+ {
+ throw new VerifySecretException(key, "Secret token is revoked.");
+ }
+
+ if (entity.ExpireTime is not null && DateTime.ParseExact(entity.ExpireTime, "O", null) > DateTime.Now)
+ {
+ throw new VerifySecretException(key, "Secret token is expired.");
+ }
+
+ if (key is not null)
+ {
+ if (entity.Key != key)
+ {
+ throw new VerifySecretException(key, "Secret is not for this key", VerifySecretException.ErrorKind.Forbidden);
+ }
+ }
}
- public Task VerifySecretForHttpRequestAsync(HttpRequest request, string key, string queryKey = "secret")
+ public async Task VerifySecretForHttpRequestAsync(HttpRequest request, string? key, string queryKey = "secret")
{
- throw new NotImplementedException();
+ string? secret = null;
+
+ var authorizationHeaders = request.Headers.Authorization.ToList();
+ if (authorizationHeaders.Count > 1)
+ {
+ _logger.LogWarning("There are multiple Authorization headers in the request. Will use the last one.");
+ }
+ if (authorizationHeaders.Count > 0)
+ {
+ var authorizationHeader = authorizationHeaders[^1] ?? "";
+ if (!authorizationHeader.StartsWith("Bearer "))
+ {
+ throw new VerifySecretException(key, "Authorization header must start with 'Bearer '.");
+ }
+
+ secret = authorizationHeader.Substring("Bearer ".Length).Trim();
+ }
+
+ var secretQueryParam = request.Query[queryKey].ToList();
+ if (secretQueryParam.Count > 1)
+ {
+ _logger.LogWarning($"There are multiple '{queryKey}' query parameters in the request. Will use the last one.");
+ }
+ if (secretQueryParam.Count > 0)
+ {
+ if (secret is not null)
+ {
+ _logger.LogWarning("Secret found both in Authorization header and query parameter. Will use the one in query parameter.");
+ }
+ secret = secretQueryParam[^1] ?? "";
+ }
+
+ await VerifySecretAsync(key, secret);
}
}