mirror of
https://github.com/bitwarden/server.git
synced 2025-07-01 16:12:49 -05:00
chore: update LastActivityDate
on installation token refresh (#5081)
This commit is contained in:
@ -1,11 +1,13 @@
|
||||
using System.Diagnostics;
|
||||
using System.Security.Claims;
|
||||
using Bit.Core;
|
||||
using Bit.Core.AdminConsole.Services;
|
||||
using Bit.Core.Auth.Models.Api.Response;
|
||||
using Bit.Core.Auth.Repositories;
|
||||
using Bit.Core.Context;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.IdentityServer;
|
||||
using Bit.Core.Platform.Installations;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Settings;
|
||||
@ -23,6 +25,7 @@ public class CustomTokenRequestValidator : BaseRequestValidator<CustomTokenReque
|
||||
ICustomTokenRequestValidator
|
||||
{
|
||||
private readonly UserManager<User> _userManager;
|
||||
private readonly IUpdateInstallationCommand _updateInstallationCommand;
|
||||
|
||||
public CustomTokenRequestValidator(
|
||||
UserManager<User> userManager,
|
||||
@ -39,7 +42,8 @@ public class CustomTokenRequestValidator : BaseRequestValidator<CustomTokenReque
|
||||
IPolicyService policyService,
|
||||
IFeatureService featureService,
|
||||
ISsoConfigRepository ssoConfigRepository,
|
||||
IUserDecryptionOptionsBuilder userDecryptionOptionsBuilder
|
||||
IUserDecryptionOptionsBuilder userDecryptionOptionsBuilder,
|
||||
IUpdateInstallationCommand updateInstallationCommand
|
||||
)
|
||||
: base(
|
||||
userManager,
|
||||
@ -59,6 +63,7 @@ public class CustomTokenRequestValidator : BaseRequestValidator<CustomTokenReque
|
||||
userDecryptionOptionsBuilder)
|
||||
{
|
||||
_userManager = userManager;
|
||||
_updateInstallationCommand = updateInstallationCommand;
|
||||
}
|
||||
|
||||
public async Task ValidateAsync(CustomTokenRequestValidationContext context)
|
||||
@ -76,16 +81,24 @@ public class CustomTokenRequestValidator : BaseRequestValidator<CustomTokenReque
|
||||
}
|
||||
|
||||
string[] allowedGrantTypes = ["authorization_code", "client_credentials"];
|
||||
string clientId = context.Result.ValidatedRequest.ClientId;
|
||||
if (!allowedGrantTypes.Contains(context.Result.ValidatedRequest.GrantType)
|
||||
|| context.Result.ValidatedRequest.ClientId.StartsWith("organization")
|
||||
|| context.Result.ValidatedRequest.ClientId.StartsWith("installation")
|
||||
|| context.Result.ValidatedRequest.ClientId.StartsWith("internal")
|
||||
|| clientId.StartsWith("organization")
|
||||
|| clientId.StartsWith("installation")
|
||||
|| clientId.StartsWith("internal")
|
||||
|| context.Result.ValidatedRequest.Client.AllowedScopes.Contains(ApiScopes.ApiSecrets))
|
||||
{
|
||||
if (context.Result.ValidatedRequest.Client.Properties.TryGetValue("encryptedPayload", out var payload) &&
|
||||
!string.IsNullOrWhiteSpace(payload))
|
||||
{
|
||||
context.Result.CustomResponse = new Dictionary<string, object> { { "encrypted_payload", payload } };
|
||||
|
||||
}
|
||||
if (FeatureService.IsEnabled(FeatureFlagKeys.RecordInstallationLastActivityDate)
|
||||
&& context.Result.ValidatedRequest.ClientId.StartsWith("installation"))
|
||||
{
|
||||
var installationIdPart = clientId.Split(".")[1];
|
||||
await RecordActivityForInstallation(clientId.Split(".")[1]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -152,6 +165,7 @@ public class CustomTokenRequestValidator : BaseRequestValidator<CustomTokenReque
|
||||
context.Result.CustomResponse["KeyConnectorUrl"] = userDecryptionOptions.KeyConnectorOption.KeyConnectorUrl;
|
||||
context.Result.CustomResponse["ResetMasterPassword"] = false;
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
@ -202,4 +216,25 @@ public class CustomTokenRequestValidator : BaseRequestValidator<CustomTokenReque
|
||||
context.Result.ErrorDescription = requestContext.ValidationErrorResult.ErrorDescription;
|
||||
context.Result.CustomResponse = requestContext.CustomResponse;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// To help mentally separate organizations that self host from abandoned
|
||||
/// organizations we hook in to the token refresh event for installations
|
||||
/// to write a simple `DateTime.Now` to the database.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This works well because installations don't phone home very often.
|
||||
/// Currently self hosted installations only refresh tokens every 24
|
||||
/// hours or so for the sake of hooking in to cloud's push relay service.
|
||||
/// If installations ever start refreshing tokens more frequently we may need to
|
||||
/// adjust this to avoid making a bunch of unnecessary database calls!
|
||||
/// </remarks>
|
||||
private async Task RecordActivityForInstallation(string? installationIdString)
|
||||
{
|
||||
if (!Guid.TryParse(installationIdString, out var installationId))
|
||||
{
|
||||
return;
|
||||
}
|
||||
await _updateInstallationCommand.UpdateLastActivityDateAsync(installationId);
|
||||
}
|
||||
}
|
||||
|
@ -44,8 +44,7 @@ public class WebAuthnGrantValidator : BaseRequestValidator<ExtensionGrantValidat
|
||||
IDataProtectorTokenFactory<WebAuthnLoginAssertionOptionsTokenable> assertionOptionsDataProtector,
|
||||
IFeatureService featureService,
|
||||
IUserDecryptionOptionsBuilder userDecryptionOptionsBuilder,
|
||||
IAssertWebAuthnLoginCredentialCommand assertWebAuthnLoginCredentialCommand
|
||||
)
|
||||
IAssertWebAuthnLoginCredentialCommand assertWebAuthnLoginCredentialCommand)
|
||||
: base(
|
||||
userManager,
|
||||
userService,
|
||||
|
Reference in New Issue
Block a user