added whitelisting checks in follows

This commit is contained in:
Nicolas Constant 2021-02-04 18:56:14 -05:00
parent 3e772f2cd4
commit 392c7ca494
No known key found for this signature in database
GPG key ID: 1E9F677FB01A5688

View file

@ -10,6 +10,7 @@ using BirdsiteLive.ActivityPub.Converters;
using BirdsiteLive.Common.Settings; using BirdsiteLive.Common.Settings;
using BirdsiteLive.Cryptography; using BirdsiteLive.Cryptography;
using BirdsiteLive.Domain.BusinessUseCases; using BirdsiteLive.Domain.BusinessUseCases;
using BirdsiteLive.Domain.Repository;
using BirdsiteLive.Domain.Statistics; using BirdsiteLive.Domain.Statistics;
using BirdsiteLive.Domain.Tools; using BirdsiteLive.Domain.Tools;
using BirdsiteLive.Twitter; using BirdsiteLive.Twitter;
@ -39,8 +40,10 @@ namespace BirdsiteLive.Domain
private readonly ITwitterUserService _twitterUserService; private readonly ITwitterUserService _twitterUserService;
private readonly IModerationRepository _moderationRepository;
#region Ctor #region Ctor
public UserService(InstanceSettings instanceSettings, ICryptoService cryptoService, IActivityPubService activityPubService, IProcessFollowUser processFollowUser, IProcessUndoFollowUser processUndoFollowUser, IStatusExtractor statusExtractor, IExtractionStatisticsHandler statisticsHandler, ITwitterUserService twitterUserService) public UserService(InstanceSettings instanceSettings, ICryptoService cryptoService, IActivityPubService activityPubService, IProcessFollowUser processFollowUser, IProcessUndoFollowUser processUndoFollowUser, IStatusExtractor statusExtractor, IExtractionStatisticsHandler statisticsHandler, ITwitterUserService twitterUserService, IModerationRepository moderationRepository)
{ {
_instanceSettings = instanceSettings; _instanceSettings = instanceSettings;
_cryptoService = cryptoService; _cryptoService = cryptoService;
@ -50,6 +53,7 @@ namespace BirdsiteLive.Domain
_statusExtractor = statusExtractor; _statusExtractor = statusExtractor;
_statisticsHandler = statisticsHandler; _statisticsHandler = statisticsHandler;
_twitterUserService = twitterUserService; _twitterUserService = twitterUserService;
_moderationRepository = moderationRepository;
} }
#endregion #endregion
@ -118,62 +122,94 @@ namespace BirdsiteLive.Domain
var sigValidation = await ValidateSignature(activity.actor, signature, method, path, queryString, requestHeaders, body); var sigValidation = await ValidateSignature(activity.actor, signature, method, path, queryString, requestHeaders, body);
if (!sigValidation.SignatureIsValidated) return false; if (!sigValidation.SignatureIsValidated) return false;
// Save Follow in DB // Prepare data
var followerUserName = sigValidation.User.preferredUsername.ToLowerInvariant(); var followerUserName = sigValidation.User.preferredUsername.ToLowerInvariant().Trim();
var followerHost = sigValidation.User.url.Replace("https://", string.Empty).Split('/').First(); var followerHost = sigValidation.User.url.Replace("https://", string.Empty).Split('/').First();
var followerInbox = sigValidation.User.inbox; var followerInbox = sigValidation.User.inbox;
var followerSharedInbox = sigValidation.User?.endpoints?.sharedInbox; var followerSharedInbox = sigValidation.User?.endpoints?.sharedInbox;
var twitterUser = activity.apObject.Split('/').Last().Replace("@", string.Empty); var twitterUser = activity.apObject.Split('/').Last().Replace("@", string.Empty).ToLowerInvariant().Trim();
// Make sure to only keep routes // Make sure to only keep routes
followerInbox = OnlyKeepRoute(followerInbox, followerHost); followerInbox = OnlyKeepRoute(followerInbox, followerHost);
followerSharedInbox = OnlyKeepRoute(followerSharedInbox, followerHost); followerSharedInbox = OnlyKeepRoute(followerSharedInbox, followerHost);
// Validate Moderation status
var followerModPolicy = _moderationRepository.GetModerationType(ModerationEntityTypeEnum.Follower);
if (followerModPolicy != ModerationTypeEnum.None)
{
var followerStatus = _moderationRepository.CheckStatus(ModerationEntityTypeEnum.Follower, $"@{followerUserName}@{followerHost}");
if(followerModPolicy == ModerationTypeEnum.WhiteListing && followerStatus != ModeratedTypeEnum.WhiteListed ||
followerModPolicy == ModerationTypeEnum.BlackListing && followerStatus == ModeratedTypeEnum.BlackListed)
return await SendRejectFollowAsync(activity, followerHost);
}
// Validate TwitterAccount status
var twitterAccountModPolicy = _moderationRepository.GetModerationType(ModerationEntityTypeEnum.TwitterAccount);
if (twitterAccountModPolicy != ModerationTypeEnum.None)
{
var twitterUserStatus = _moderationRepository.CheckStatus(ModerationEntityTypeEnum.TwitterAccount, twitterUser);
if (twitterAccountModPolicy == ModerationTypeEnum.WhiteListing && twitterUserStatus != ModeratedTypeEnum.WhiteListed ||
twitterAccountModPolicy == ModerationTypeEnum.BlackListing && twitterUserStatus == ModeratedTypeEnum.BlackListed)
return await SendRejectFollowAsync(activity, followerHost);
}
// Validate User Protected
var user = _twitterUserService.GetUser(twitterUser); var user = _twitterUserService.GetUser(twitterUser);
if (!user.Protected) if (!user.Protected)
{ {
// Execute // Execute
await _processFollowUser.ExecuteAsync(followerUserName, followerHost, twitterUser, followerInbox, followerSharedInbox); await _processFollowUser.ExecuteAsync(followerUserName, followerHost, twitterUser, followerInbox, followerSharedInbox);
// Send Accept Activity return await SendAcceptFollowAsync(activity, followerHost);
var acceptFollow = new ActivityAcceptFollow()
{
context = "https://www.w3.org/ns/activitystreams",
id = $"{activity.apObject}#accepts/follows/{Guid.NewGuid()}",
type = "Accept",
actor = activity.apObject,
apObject = new ActivityFollow()
{
id = activity.id,
type = activity.type,
actor = activity.actor,
apObject = activity.apObject
}
};
var result = await _activityPubService.PostDataAsync(acceptFollow, followerHost, activity.apObject);
return result == HttpStatusCode.Accepted || result == HttpStatusCode.OK; //TODO: revamp this for better error handling
} }
else else
{ {
// Send Reject Activity return await SendRejectFollowAsync(activity, followerHost);
var acceptFollow = new ActivityRejectFollow()
{
context = "https://www.w3.org/ns/activitystreams",
id = $"{activity.apObject}#rejects/follows/{Guid.NewGuid()}",
type = "Reject",
actor = activity.apObject,
apObject = new ActivityFollow()
{
id = activity.id,
type = activity.type,
actor = activity.actor,
apObject = activity.apObject
}
};
var result = await _activityPubService.PostDataAsync(acceptFollow, followerHost, activity.apObject);
return result == HttpStatusCode.Accepted || result == HttpStatusCode.OK; //TODO: revamp this for better error handling
} }
} }
private async Task<bool> SendAcceptFollowAsync(ActivityFollow activity, string followerHost)
{
var acceptFollow = new ActivityAcceptFollow()
{
context = "https://www.w3.org/ns/activitystreams",
id = $"{activity.apObject}#accepts/follows/{Guid.NewGuid()}",
type = "Accept",
actor = activity.apObject,
apObject = new ActivityFollow()
{
id = activity.id,
type = activity.type,
actor = activity.actor,
apObject = activity.apObject
}
};
var result = await _activityPubService.PostDataAsync(acceptFollow, followerHost, activity.apObject);
return result == HttpStatusCode.Accepted ||
result == HttpStatusCode.OK; //TODO: revamp this for better error handling
}
private async Task<bool> SendRejectFollowAsync(ActivityFollow activity, string followerHost)
{
var acceptFollow = new ActivityRejectFollow()
{
context = "https://www.w3.org/ns/activitystreams",
id = $"{activity.apObject}#rejects/follows/{Guid.NewGuid()}",
type = "Reject",
actor = activity.apObject,
apObject = new ActivityFollow()
{
id = activity.id,
type = activity.type,
actor = activity.actor,
apObject = activity.apObject
}
};
var result = await _activityPubService.PostDataAsync(acceptFollow, followerHost, activity.apObject);
return result == HttpStatusCode.Accepted ||
result == HttpStatusCode.OK; //TODO: revamp this for better error handling
}
private string OnlyKeepRoute(string inbox, string host) private string OnlyKeepRoute(string inbox, string host)
{ {