diff --git a/src/BirdsiteLive.Twitter/BirdsiteLive.Twitter.csproj b/src/BirdsiteLive.Twitter/BirdsiteLive.Twitter.csproj
index 377d04d..438b4e1 100644
--- a/src/BirdsiteLive.Twitter/BirdsiteLive.Twitter.csproj
+++ b/src/BirdsiteLive.Twitter/BirdsiteLive.Twitter.csproj
@@ -13,4 +13,8 @@
+
+
+
+
diff --git a/src/BirdsiteLive.Twitter/Tools/TwitterAuthenticationInitializer.cs b/src/BirdsiteLive.Twitter/Tools/TwitterAuthenticationInitializer.cs
new file mode 100644
index 0000000..8484c3d
--- /dev/null
+++ b/src/BirdsiteLive.Twitter/Tools/TwitterAuthenticationInitializer.cs
@@ -0,0 +1,64 @@
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using BirdsiteLive.Common.Settings;
+using Microsoft.Extensions.Logging;
+using Tweetinvi;
+
+namespace BirdsiteLive.Twitter.Tools
+{
+ public interface ITwitterAuthenticationInitializer
+ {
+ void EnsureAuthenticationIsInitialized();
+ }
+
+ public class TwitterAuthenticationInitializer : ITwitterAuthenticationInitializer
+ {
+ private readonly TwitterSettings _settings;
+ private readonly ILogger _logger;
+ private static bool _initialized;
+ private readonly SemaphoreSlim _semaphoregate = new SemaphoreSlim(1);
+
+ #region Ctor
+ public TwitterAuthenticationInitializer(TwitterSettings settings, ILogger logger)
+ {
+ _settings = settings;
+ _logger = logger;
+ }
+ #endregion
+
+ public void EnsureAuthenticationIsInitialized()
+ {
+ if (_initialized) return;
+ _semaphoregate.Wait();
+
+ try
+ {
+ if (_initialized) return;
+ InitTwitterCredentials();
+ }
+ finally
+ {
+ _semaphoregate.Release();
+ }
+ }
+
+ private void InitTwitterCredentials()
+ {
+ for (;;)
+ {
+ try
+ {
+ Auth.SetApplicationOnlyCredentials(_settings.ConsumerKey, _settings.ConsumerSecret, true);
+ _initialized = true;
+ return;
+ }
+ catch (Exception e)
+ {
+ _logger.LogError(e, "Twitter Authentication Failed");
+ Thread.Sleep(250);
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/BirdsiteLive.Twitter/TwitterTweetsService.cs b/src/BirdsiteLive.Twitter/TwitterTweetsService.cs
index e67b125..49684e8 100644
--- a/src/BirdsiteLive.Twitter/TwitterTweetsService.cs
+++ b/src/BirdsiteLive.Twitter/TwitterTweetsService.cs
@@ -5,6 +5,7 @@ using BirdsiteLive.Common.Settings;
using BirdsiteLive.Statistics.Domain;
using BirdsiteLive.Twitter.Extractors;
using BirdsiteLive.Twitter.Models;
+using BirdsiteLive.Twitter.Tools;
using Microsoft.Extensions.Logging;
using Tweetinvi;
using Tweetinvi.Models;
@@ -20,22 +21,20 @@ namespace BirdsiteLive.Twitter
public class TwitterTweetsService : ITwitterTweetsService
{
- private readonly TwitterSettings _settings;
+ private readonly ITwitterAuthenticationInitializer _twitterAuthenticationInitializer;
private readonly ITweetExtractor _tweetExtractor;
private readonly ITwitterStatisticsHandler _statisticsHandler;
private readonly ITwitterUserService _twitterUserService;
private readonly ILogger _logger;
#region Ctor
- public TwitterTweetsService(TwitterSettings settings, ITweetExtractor tweetExtractor, ITwitterStatisticsHandler statisticsHandler, ITwitterUserService twitterUserService, ILogger logger)
+ public TwitterTweetsService(ITwitterAuthenticationInitializer twitterAuthenticationInitializer, ITweetExtractor tweetExtractor, ITwitterStatisticsHandler statisticsHandler, ITwitterUserService twitterUserService, ILogger logger)
{
- _settings = settings;
+ _twitterAuthenticationInitializer = twitterAuthenticationInitializer;
_tweetExtractor = tweetExtractor;
_statisticsHandler = statisticsHandler;
_twitterUserService = twitterUserService;
_logger = logger;
- Auth.SetApplicationOnlyCredentials(_settings.ConsumerKey, _settings.ConsumerSecret, true);
- ExceptionHandler.SwallowWebExceptions = false;
}
#endregion
@@ -43,7 +42,10 @@ namespace BirdsiteLive.Twitter
{
try
{
+ _twitterAuthenticationInitializer.EnsureAuthenticationIsInitialized();
+ ExceptionHandler.SwallowWebExceptions = false;
TweetinviConfig.CurrentThreadSettings.TweetMode = TweetMode.Extended;
+
var tweet = Tweet.GetTweet(statusId);
_statisticsHandler.CalledTweetApi();
if (tweet == null) return null; //TODO: test this
@@ -58,14 +60,16 @@ namespace BirdsiteLive.Twitter
public ExtractedTweet[] GetTimeline(string username, int nberTweets, long fromTweetId = -1)
{
- TweetinviConfig.CurrentThreadSettings.TweetMode = TweetMode.Extended;
-
- var user = _twitterUserService.GetUser(username);
- if (user.Protected) return new ExtractedTweet[0];
-
var tweets = new List();
try
{
+ _twitterAuthenticationInitializer.EnsureAuthenticationIsInitialized();
+ ExceptionHandler.SwallowWebExceptions = false;
+ TweetinviConfig.CurrentThreadSettings.TweetMode = TweetMode.Extended;
+
+ var user = _twitterUserService.GetUser(username);
+ if (user == null || user.Protected) return new ExtractedTweet[0];
+
if (fromTweetId == -1)
{
var timeline = Timeline.GetUserTimeline(user.Id, nberTweets);
diff --git a/src/BirdsiteLive.Twitter/TwitterUserService.cs b/src/BirdsiteLive.Twitter/TwitterUserService.cs
index e7d44c3..dd80c4d 100644
--- a/src/BirdsiteLive.Twitter/TwitterUserService.cs
+++ b/src/BirdsiteLive.Twitter/TwitterUserService.cs
@@ -3,6 +3,7 @@ using System.Linq;
using BirdsiteLive.Common.Settings;
using BirdsiteLive.Statistics.Domain;
using BirdsiteLive.Twitter.Models;
+using BirdsiteLive.Twitter.Tools;
using Microsoft.Extensions.Logging;
using Tweetinvi;
using Tweetinvi.Models;
@@ -16,29 +17,34 @@ namespace BirdsiteLive.Twitter
public class TwitterUserService : ITwitterUserService
{
- private readonly TwitterSettings _settings;
+ private readonly ITwitterAuthenticationInitializer _twitterAuthenticationInitializer;
private readonly ITwitterStatisticsHandler _statisticsHandler;
private readonly ILogger _logger;
#region Ctor
- public TwitterUserService(TwitterSettings settings, ITwitterStatisticsHandler statisticsHandler, ILogger logger)
+ public TwitterUserService(ITwitterAuthenticationInitializer twitterAuthenticationInitializer, ITwitterStatisticsHandler statisticsHandler, ILogger logger)
{
- _settings = settings;
+ _twitterAuthenticationInitializer = twitterAuthenticationInitializer;
_statisticsHandler = statisticsHandler;
_logger = logger;
- Auth.SetApplicationOnlyCredentials(_settings.ConsumerKey, _settings.ConsumerSecret, true);
- ExceptionHandler.SwallowWebExceptions = false;
}
#endregion
public TwitterUser GetUser(string username)
{
+ _twitterAuthenticationInitializer.EnsureAuthenticationIsInitialized();
+ ExceptionHandler.SwallowWebExceptions = false;
+
IUser user;
try
{
user = User.GetUserFromScreenName(username);
_statisticsHandler.CalledUserApi();
- if (user == null) return null;
+ if (user == null)
+ {
+ _logger.LogWarning("User {username} not found", username);
+ return null;
+ }
}
catch (Exception e)
{
diff --git a/src/BirdsiteLive/BirdsiteLive.csproj b/src/BirdsiteLive/BirdsiteLive.csproj
index 3766f3a..cef4948 100644
--- a/src/BirdsiteLive/BirdsiteLive.csproj
+++ b/src/BirdsiteLive/BirdsiteLive.csproj
@@ -4,7 +4,7 @@
netcoreapp3.1
d21486de-a812-47eb-a419-05682bb68856
Linux
- 0.12.1
+ 0.12.2
diff --git a/src/BirdsiteLive/Controllers/UsersController.cs b/src/BirdsiteLive/Controllers/UsersController.cs
index d291c67..a22ae73 100644
--- a/src/BirdsiteLive/Controllers/UsersController.cs
+++ b/src/BirdsiteLive/Controllers/UsersController.cs
@@ -4,6 +4,7 @@ using System.IO;
using System.Linq;
using System.Net.Mime;
using System.Runtime.InteropServices.WindowsRuntime;
+using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using BirdsiteLive.ActivityPub;
@@ -12,6 +13,7 @@ using BirdsiteLive.Common.Settings;
using BirdsiteLive.Domain;
using BirdsiteLive.Models;
using BirdsiteLive.Twitter;
+using BirdsiteLive.Twitter.Models;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Primitives;
@@ -26,6 +28,7 @@ namespace BirdsiteLive.Controllers
private readonly IUserService _userService;
private readonly IStatusService _statusService;
private readonly InstanceSettings _instanceSettings;
+ private readonly Regex _twitterAccountRegex = new Regex(@"^[a-zA-Z0-9_]+$");
#region Ctor
public UsersController(ITwitterUserService twitterUserService, IUserService userService, IStatusService statusService, InstanceSettings instanceSettings, ITwitterTweetsService twitterTweetService)
@@ -55,7 +58,12 @@ namespace BirdsiteLive.Controllers
public IActionResult Index(string id)
{
id = id.Trim(new[] { ' ', '@' }).ToLowerInvariant();
- var user = _twitterUserService.GetUser(id);
+
+ // Ensure valid username
+ // https://help.twitter.com/en/managing-your-account/twitter-username-rules
+ TwitterUser user = null;
+ if (!string.IsNullOrWhiteSpace(id) && _twitterAccountRegex.IsMatch(id) && id.Length <= 15)
+ user = _twitterUserService.GetUser(id);
var acceptHeaders = Request.Headers["Accept"];
if (acceptHeaders.Any())
diff --git a/src/BirdsiteLive/Controllers/WellKnownController.cs b/src/BirdsiteLive/Controllers/WellKnownController.cs
index 553d8e7..3f060a7 100644
--- a/src/BirdsiteLive/Controllers/WellKnownController.cs
+++ b/src/BirdsiteLive/Controllers/WellKnownController.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Text.RegularExpressions;
using System.Threading.Tasks;
using BirdsiteLive.ActivityPub.Converters;
using BirdsiteLive.Common.Settings;
@@ -19,6 +20,7 @@ namespace BirdsiteLive.Controllers
private readonly ITwitterUserService _twitterUserService;
private readonly ITwitterUserDal _twitterUserDal;
private readonly InstanceSettings _settings;
+ private readonly Regex _twitterAccountRegex = new Regex(@"^[a-zA-Z0-9_]+$");
#region Ctor
public WellKnownController(InstanceSettings settings, ITwitterUserService twitterUserService, ITwitterUserDal twitterUserDal)
@@ -160,6 +162,11 @@ namespace BirdsiteLive.Controllers
// Ensure lowercase
name = name.ToLowerInvariant();
+ // Ensure valid username
+ // https://help.twitter.com/en/managing-your-account/twitter-username-rules
+ if (string.IsNullOrWhiteSpace(name) || !_twitterAccountRegex.IsMatch(name) || name.Length > 15 )
+ return NotFound();
+
if (!string.IsNullOrWhiteSpace(domain) && domain != _settings.Domain)
return NotFound();
diff --git a/src/BirdsiteLive/Startup.cs b/src/BirdsiteLive/Startup.cs
index 371e969..ab50932 100644
--- a/src/BirdsiteLive/Startup.cs
+++ b/src/BirdsiteLive/Startup.cs
@@ -10,6 +10,7 @@ using BirdsiteLive.DAL.Postgres.DataAccessLayers;
using BirdsiteLive.DAL.Postgres.Settings;
using BirdsiteLive.Models;
using BirdsiteLive.Twitter;
+using BirdsiteLive.Twitter.Tools;
using Lamar;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
@@ -87,6 +88,8 @@ namespace BirdsiteLive
services.For().DecorateAllWith();
services.For().Use().Singleton();
+ services.For().Use().Singleton();
+
services.Scan(_ =>
{
_.Assembly("BirdsiteLive.Twitter");