mirror of
https://github.com/bitwarden/server.git
synced 2025-06-06 11:10:32 -05:00
PM-20532 - SendAccessGrantValidator.cs - integrate call to sendAuthenticationQuery
This commit is contained in:
parent
0f8d11c124
commit
a421d334a4
@ -1,21 +1,24 @@
|
|||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using Bit.Core.Entities;
|
using Bit.Core.Entities;
|
||||||
using Bit.Core.Identity;
|
using Bit.Core.Identity;
|
||||||
using Bit.Core.Tools.Repositories;
|
using Bit.Core.Tools.Models.Data;
|
||||||
|
using Bit.Core.Tools.SendFeatures.Queries.Interfaces;
|
||||||
|
using Bit.Core.Utilities;
|
||||||
using Duende.IdentityServer.Models;
|
using Duende.IdentityServer.Models;
|
||||||
using Duende.IdentityServer.Validation;
|
using Duende.IdentityServer.Validation;
|
||||||
using Microsoft.AspNetCore.Identity;
|
using Microsoft.AspNetCore.Identity;
|
||||||
|
|
||||||
namespace Bit.Identity.IdentityServer.RequestValidators;
|
namespace Bit.Identity.IdentityServer.RequestValidators;
|
||||||
|
|
||||||
public class SendAccessGrantValidator(ISendRepository sendRepository, IPasswordHasher<User> passwordHasher) : IExtensionGrantValidator
|
public class SendAccessGrantValidator(ISendAuthenticationQuery sendAuthenticationQuery, IPasswordHasher<User> passwordHasher) : IExtensionGrantValidator
|
||||||
{
|
{
|
||||||
public const string GrantType = "send_access";
|
public const string GrantType = "send_access";
|
||||||
|
|
||||||
string IExtensionGrantValidator.GrantType => GrantType;
|
string IExtensionGrantValidator.GrantType => GrantType;
|
||||||
|
|
||||||
private const string _invalidRequestMissingSendIdMessage = "Invalid request. send_id is required.";
|
private const string _invalidRequestMissingSendIdMessage = "send_id is required.";
|
||||||
private const string _invalidRequestPasswordRequiredMessage = "Invalid request. Password is required.";
|
private const string _invalidRequestPasswordRequiredMessage = "Password is required.";
|
||||||
|
private const string _invalidRequestEmailOtpRequiredMessage = "Email and OTP are required.";
|
||||||
private const string _invalidGrantPasswordInvalid = "Password invalid.";
|
private const string _invalidGrantPasswordInvalid = "Password invalid.";
|
||||||
// TODO: add email OTP validation error messages here.
|
// TODO: add email OTP validation error messages here.
|
||||||
|
|
||||||
@ -31,94 +34,63 @@ public class SendAccessGrantValidator(ISendRepository sendRepository, IPasswordH
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Guid.TryParse(sendId, out var sendIdGuid))
|
var sendIdGuid = new Guid(CoreHelpers.Base64UrlDecode(sendId));
|
||||||
|
|
||||||
|
if (sendIdGuid == Guid.Empty)
|
||||||
{
|
{
|
||||||
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidRequest, errorDescription: _invalidRequestMissingSendIdMessage);
|
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidRequest, errorDescription: _invalidRequestMissingSendIdMessage);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: replace repository look up & following logic with use of SendAuthenticationQuery.GetAuthenticationMethod from Tools
|
|
||||||
// See below for example of consumption of SendAuthQuery.GetAuthenticationMethod(sendId)
|
|
||||||
|
|
||||||
// Look up send by id
|
// Look up send by id
|
||||||
var send = await sendRepository.GetByIdAsync(sendIdGuid);
|
var method = await sendAuthenticationQuery.GetAuthenticationMethod(sendIdGuid);
|
||||||
|
|
||||||
if (send == null)
|
switch (method)
|
||||||
{
|
{
|
||||||
// TODO: Add send enumeration protection here (primarily benefits self hosted instances).
|
case NeverAuthenticate:
|
||||||
// We should only map to password or email + OTP protected. If user submits password guess for a
|
// null send scenario.
|
||||||
// falsely protected send, then we will return invalid password.
|
// TODO: Add send enumeration protection here (primarily benefits self hosted instances).
|
||||||
// TODO: we should re-use _invalidGrantPasswordInvalid or similar error message here.
|
// We should only map to password or email + OTP protected. If user submits password guess for a
|
||||||
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidRequest, "Invalid request");
|
// falsely protected send, then we will return invalid password.
|
||||||
return;
|
// TODO: we should re-use _invalidGrantPasswordInvalid or similar error message here.
|
||||||
}
|
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidRequest, "Invalid request");
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(send.Password))
|
|
||||||
{
|
|
||||||
// Send is password protected so we need to validate the password.
|
|
||||||
var password = request.Get("password");
|
|
||||||
if (string.IsNullOrEmpty(password))
|
|
||||||
{
|
|
||||||
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidRequest, errorDescription: _invalidRequestPasswordRequiredMessage);
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
var passwordValid = ValidateSendPassword(send.Password, password);
|
case NotAuthenticated:
|
||||||
|
// automatically issue access token
|
||||||
if (!passwordValid)
|
context.Result = BuildBaseSuccessResult(sendId);
|
||||||
{
|
|
||||||
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, errorDescription: _invalidGrantPasswordInvalid);
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
// password is valid, so we can issue an access token.
|
case ResourcePassword rp:
|
||||||
context.Result = BuildBaseSuccessResult(sendId);
|
var password = request.Get("password_hash");
|
||||||
return;
|
|
||||||
|
if (string.IsNullOrEmpty(password))
|
||||||
|
{
|
||||||
|
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidRequest, errorDescription: _invalidRequestPasswordRequiredMessage);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var passwordValid = ValidateSendPassword(rp.Hash, password);
|
||||||
|
|
||||||
|
if (!passwordValid)
|
||||||
|
{
|
||||||
|
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, errorDescription: _invalidGrantPasswordInvalid);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// password is valid, so we can issue an access token.
|
||||||
|
context.Result = BuildBaseSuccessResult(sendId);
|
||||||
|
return;
|
||||||
|
|
||||||
|
case EmailOtp eo:
|
||||||
|
// TODO: We will either send the OTP here or validate it based on if otp exists in the request.
|
||||||
|
// SendOtpToEmail(eo.Emails) or ValidateOtp(eo.Emails);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
// shouldn’t ever hit this
|
||||||
|
throw new InvalidOperationException($"Unknown auth method: {method.GetType()}");
|
||||||
}
|
}
|
||||||
|
|
||||||
// if send is anon, provide access token
|
|
||||||
|
|
||||||
context.Result = BuildBaseSuccessResult(sendId);
|
|
||||||
|
|
||||||
|
|
||||||
// Email + OTP - if we generate OTP here, we could run into rate limiting issues with re-hitting this endpoint
|
|
||||||
// We will generate & validate OTP here.
|
|
||||||
|
|
||||||
|
|
||||||
// if send is password protected, check if password is provided and validate if so
|
|
||||||
// if send is email + OTP protected, check if email and OTP are provided and validate if so
|
|
||||||
|
|
||||||
// TODO: Example Consumption of SendAuthQuery.GetAuthenticationMethod(sendId) to replace above logic.
|
|
||||||
|
|
||||||
// var method = await sendAuthQuery.GetAuthenticationMethod(sendId);
|
|
||||||
//
|
|
||||||
// switch (method)
|
|
||||||
// {
|
|
||||||
// case NeverAuthenticate:
|
|
||||||
// // null send scenario.
|
|
||||||
// HandleNullSend(); // this is where we add send enumeration protection
|
|
||||||
// break;
|
|
||||||
//
|
|
||||||
// case NotAuthenticated:
|
|
||||||
// // automatically issue access token
|
|
||||||
// break;
|
|
||||||
//
|
|
||||||
// case ResourcePassword rp:
|
|
||||||
// ValidatePassword(rp.Hash);
|
|
||||||
// break;
|
|
||||||
//
|
|
||||||
// case EmailOtp eo:
|
|
||||||
// We will either send the OTP here or validate it.
|
|
||||||
// SendOtpToEmails(eo.Emails) or ValidateOtp(eo.Emails);
|
|
||||||
// break;
|
|
||||||
//
|
|
||||||
// default:
|
|
||||||
// // shouldn’t ever hit this
|
|
||||||
// throw new InvalidOperationException($"Unknown auth method: {method.GetType()}");
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private GrantValidationResult BuildBaseSuccessResult(string sendId)
|
private GrantValidationResult BuildBaseSuccessResult(string sendId)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user