public async Task <IActionResult> Create(LinkEditModel model) { if (ModelState.IsValid) { var verifyResult = _linkVerifier.Verify(model.OriginUrl, Url, Request); switch (verifyResult) { case LinkVerifyResult.InvalidFormat: return(BadRequest("Not a valid URL.")); case LinkVerifyResult.InvalidLocal: return(BadRequest("Can not use local URL.")); case LinkVerifyResult.InvalidSelfReference: return(BadRequest("Can not use url pointing to this site.")); } var createLinkRequest = new CreateLinkRequest { OriginUrl = model.OriginUrl, Note = model.Note, AkaName = string.IsNullOrWhiteSpace(model.AkaName) ? null : model.AkaName, IsEnabled = model.IsEnabled, TTL = model.TTL }; var response = await _linkForwarderService.CreateLinkAsync(createLinkRequest); return(Json(response)); } return(BadRequest("Invalid ModelState")); }
private async Task<IActionResult> PerformTokenRedirection(string token, string ip, StringValues ua) { bool isValid = _tokenGenerator.TryParseToken(token, out var validatedToken); if (!isValid) { _logger.LogWarning($"'{ip}' requested invalid token '{token}'. Request is blocked."); return BadRequest(); } if (!_cache.TryGetValue(token, out Link linkEntry)) { var response = await _linkForwarderService.GetLinkAsync(validatedToken); if (response.IsSuccess) { var link = response.Item; if (null == link) { if (string.IsNullOrWhiteSpace(_appSettings.DefaultRedirectionUrl)) return NotFound(); var verifyDefaultRedirectionUrl = _linkVerifier.Verify(_appSettings.DefaultRedirectionUrl, Url, Request); if (verifyDefaultRedirectionUrl == LinkVerifyResult.Valid) { return Redirect(_appSettings.DefaultRedirectionUrl); } throw new UriFormatException("DefaultRedirectionUrl is not a valid URL."); } if (!link.IsEnabled) { return BadRequest("This link is disabled."); } var verifyOriginUrl = _linkVerifier.Verify(link.OriginUrl, Url, Request); switch (verifyOriginUrl) { case LinkVerifyResult.Valid: // cache valid link entity only. if (null != link.TTL) { _cache.Set(token, link, TimeSpan.FromSeconds(link.TTL.GetValueOrDefault())); } break; case LinkVerifyResult.InvalidFormat: throw new UriFormatException( $"OriginUrl '{link.OriginUrl}' is not a valid URL, link ID: {link.Id}."); case LinkVerifyResult.InvalidLocal: _logger.LogWarning($"Local redirection is blocked. link: {JsonSerializer.Serialize(link)}"); return BadRequest("Local redirection is blocked"); case LinkVerifyResult.InvalidSelfReference: _logger.LogWarning( $"Self reference redirection is blocked. link: {JsonSerializer.Serialize(link)}"); return BadRequest("Self reference redirection is blocked"); default: throw new ArgumentOutOfRangeException(); } } else { return new StatusCodeResult(StatusCodes.Status500InternalServerError); } } if (null == linkEntry) { linkEntry = _cache.Get<Link>(token); } if (_appSettings.HonorDNT) { // Check if browser sends "Do Not Track" var dntFlag = Request.Headers["DNT"]; bool dnt = !string.IsNullOrWhiteSpace(dntFlag) && dntFlag == 1.ToString(); if (!dnt) { _ = Task.Run(async () => { await _linkForwarderService.TrackSucessRedirectionAsync( new LinkTrackingRequest(ip, ua, linkEntry.Id)); }); } } return Redirect(linkEntry.OriginUrl); }
public async Task <IActionResult> Forward(string token) { try { if (string.IsNullOrWhiteSpace(token)) { return(BadRequest()); } var ip = HttpContext.Connection.RemoteIpAddress.ToString(); var ua = Request.Headers["User-Agent"]; if (string.IsNullOrWhiteSpace(ua)) { _logger.LogWarning($"'{ip}' requested token '{token}' without User Agent. Request is blocked."); return(BadRequest()); } bool isValid = _tokenGenerator.TryParseToken(token, out var validatedToken); if (!isValid) { _logger.LogWarning($"'{ip}' requested invalid token '{token}'. Request is blocked."); return(BadRequest()); } if (!_cache.TryGetValue(token, out Link linkEntry)) { var response = await _linkForwarderService.GetLinkAsync(validatedToken); if (response.IsSuccess) { var link = response.Item; if (null == link) { if (string.IsNullOrWhiteSpace(_appSettings.DefaultRedirectionUrl)) { return(NotFound()); } var verifyDefaultRedirectionUrl = _linkVerifier.Verify(_appSettings.DefaultRedirectionUrl, Url, Request); if (verifyDefaultRedirectionUrl == LinkVerifyResult.Valid) { return(Redirect(_appSettings.DefaultRedirectionUrl)); } throw new UriFormatException("DefaultRedirectionUrl is not a valid URL."); } if (!link.IsEnabled) { return(BadRequest("This link is disabled.")); } var verifyOriginUrl = _linkVerifier.Verify(link.OriginUrl, Url, Request); switch (verifyOriginUrl) { case LinkVerifyResult.Valid: // cache valid link entity only. _cache.Set(token, link, TimeSpan.FromHours(1)); break; case LinkVerifyResult.InvalidFormat: throw new UriFormatException( $"OriginUrl '{link.OriginUrl}' is not a valid URL, link ID: {link.Id}."); case LinkVerifyResult.InvalidLocal: _logger.LogWarning($"Local redirection is blocked. link: {JsonConvert.SerializeObject(link)}"); return(BadRequest("Local redirection is blocked")); case LinkVerifyResult.InvalidSelfReference: _logger.LogWarning($"Self reference redirection is blocked. link: {JsonConvert.SerializeObject(link)}"); return(BadRequest("Self reference redirection is blocked")); default: throw new ArgumentOutOfRangeException(); } } else { return(new StatusCodeResult(StatusCodes.Status500InternalServerError)); } } if (null == linkEntry) { linkEntry = _cache.Get <Link>(token); } _ = Task.Run(async() => { await _linkForwarderService.TrackSucessRedirectionAsync(ip, ua, linkEntry.Id); }); return(Redirect(linkEntry.OriginUrl)); } catch (Exception e) { _logger.LogError(e, e.Message); return(new StatusCodeResult(StatusCodes.Status500InternalServerError)); } }
private async Task <IActionResult> PerformTokenRedirection(string token, string ip) { var isValid = _tokenGenerator.TryParseToken(token, out var validatedToken); if (!isValid) { return(BadRequest()); } if (!_cache.TryGetValue(token, out Link linkEntry)) { var flag = await _featureManager.IsEnabledAsync(nameof(FeatureFlags.AllowSelfRedirection)); var link = await _linkForwarderService.GetLinkAsync(validatedToken); if (link is null) { if (string.IsNullOrWhiteSpace(_appSettings.DefaultRedirectionUrl)) { return(NotFound()); } var result = _linkVerifier.Verify(_appSettings.DefaultRedirectionUrl, Url, Request, flag); if (result == LinkVerifyResult.Valid) { return(Redirect(_appSettings.DefaultRedirectionUrl)); } throw new UriFormatException("DefaultRedirectionUrl is not a valid URL."); } if (!link.IsEnabled) { return(BadRequest("This link is disabled.")); } var verifyOriginUrl = _linkVerifier.Verify(link.OriginUrl, Url, Request, flag); switch (verifyOriginUrl) { case LinkVerifyResult.Valid: // cache valid link entity only. if (link.TTL is not null) { _cache.Set(token, link, TimeSpan.FromSeconds(link.TTL.GetValueOrDefault())); } break; case LinkVerifyResult.InvalidFormat: throw new UriFormatException( $"OriginUrl '{link.OriginUrl}' is not a valid URL, link ID: {link.Id}."); case LinkVerifyResult.InvalidLocal: _logger.LogWarning($"Local redirection is blocked. link: {JsonSerializer.Serialize(link)}"); return(BadRequest("Local redirection is blocked")); case LinkVerifyResult.InvalidSelfReference: _logger.LogWarning( $"Self reference redirection is blocked. link: {JsonSerializer.Serialize(link)}"); return(BadRequest("Self reference redirection is blocked")); default: throw new ArgumentOutOfRangeException(); } } linkEntry ??= _cache.Get <Link>(token); var honorDNTFlag = await _featureManager.IsEnabledAsync(nameof(FeatureFlags.HonorDNT)); if (!honorDNTFlag) { return(Redirect(linkEntry.OriginUrl)); } // Check if browser sends "Do Not Track" var dntFlag = Request.Headers["DNT"]; var dnt = !string.IsNullOrWhiteSpace(dntFlag) && dntFlag == "1"; if (dnt) { return(Redirect(linkEntry.OriginUrl)); } try { var req = new LinkTrackingRequest(ip, UserAgent, linkEntry.Id); await _linkForwarderService.TrackSucessRedirectionAsync(req); } catch (Exception e) { // Eat exception, pretend everything is fine // Do not block workflow here _logger.LogError(e.Message, e); } return(Redirect(linkEntry.OriginUrl)); }