Example #1
0
        public async Task PublishesDomainMissingMessageWhenDomainExistsAndRecordsStale()
        {
            EntityDkimEntityState dkimInfoResponse = GetEntityDkimEntityState(DateTime.UnixEpoch.AddDays(-2));

            A.CallTo(() => _clock.GetDateTimeUtc()).Returns(DateTime.UnixEpoch);
            A.CallTo(() => _dao.GetDkimSelectors("testDomain")).Returns(Task.FromResult(dkimInfoResponse));

            EntityDkimEntityState result = await _dkimService.GetDkimForDomain("testDomain");

            A.CallTo(() => _messagePublisher.Publish(A <DomainMissing> ._, A <string> ._))
            .MustHaveHappenedOnceExactly();
            Assert.AreSame(dkimInfoResponse, result);
        }
        public void ContainsIsPositiveForItemInsideValidityPeriod()
        {
            A.CallTo(() => _clock.GetDateTimeUtc()).Returns(DateTime.UnixEpoch);
            _recentlyProcessedLedger.Set("testHost");

            A.CallTo(() => _clock.GetDateTimeUtc()).Returns(DateTime.UnixEpoch.AddSeconds(ValidityPeriod - 1));
            Assert.True(_recentlyProcessedLedger.Contains("testHost"));
        }
        public bool Contains(string host)
        {
            _log.LogDebug($"Searching ledger for host: {host}");

            DateTime now = _clock.GetDateTimeUtc();

            if (_ledgerItems.TryGetValue(host, out DateTime value) && value > now)
            {
                _log.LogDebug($"Host {host} found in ledger with TTL of {(_ledgerItems[host] - now).TotalSeconds} seconds");
                return(true);
            }

            _log.LogDebug($"Host {host} not found in ledger");
            return(false);
        }
Example #4
0
        public async Task ShouldSaveAndDispatchMxEntityCreatedWhenDomainCreatedReceivedAndDomainDoesNotExists()
        {
            string domainName  = "testDomainName";
            string snsTopicArn = "SnsTopicArn";

            A.CallTo(() => _dao.Get(domainName.ToLower())).Returns(Task.FromResult((MxEntityState)null));
            A.CallTo(() => _mxEntityConfig.SnsTopicArn).Returns(snsTopicArn);
            A.CallTo(() => _clock.GetDateTimeUtc()).Returns(DateTime.MinValue);

            await _mxEntity.Handle(new DomainCreated(domainName, string.Empty, DateTime.MaxValue));

            A.CallTo(() => _dao.Save(A <MxEntityState> .That.Matches(state => state.MxState == MxState.Created))).MustHaveHappenedOnceExactly();
            A.CallTo(() => _dispatcher.Dispatch(A <MxEntityCreated> .That.Matches(entity => entity.Id == domainName.ToLower()), snsTopicArn)).MustHaveHappenedOnceExactly();
            A.CallTo(() => _dispatcher.Dispatch(A <CreateScheduledReminder> .That.Matches(a => a.ResourceId == domainName.ToLower() && a.Service == "Mx" && a.ScheduledTime == DateTime.MinValue), snsTopicArn)).MustHaveHappenedOnceExactly();
        }
        public void Setup()
        {
            _asInfoProvider     = A.Fake <IAsInfoProvider>();
            _blocklistProvider  = A.Fake <IBlocklistProvider>();
            _reverseDnsProvider = A.Fake <IReverseDnsProvider>();
            _log   = A.Fake <ILogger <IpAddressLookup> >();
            _clock = A.Fake <IClock>();

            _ipAddressLookup = new IpAddressLookup(_asInfoProvider, _blocklistProvider, _reverseDnsProvider, _clock, _log);

            A.CallTo(() => _blocklistProvider.GetBlocklistAppearances(A <List <string> > ._)).ReturnsLazily((List <string> arguments) =>
            {
                return(Task.FromResult(arguments.Select(x => new BlocklistResult(x, new List <BlocklistAppearance> {
                    new BlocklistAppearance("flag", "source", Guid.NewGuid().ToString())
                })
                                                        ).ToList()));
            });

            A.CallTo(() => _reverseDnsProvider.GetReverseDnsResult(A <List <string> > ._)).ReturnsLazily((List <string> arguments) =>
            {
                return(Task.FromResult(arguments.Select(x => new ReverseDnsResult(x, new List <ReverseDnsResponse>
                {
                    new ReverseDnsResponse(Guid.NewGuid().ToString(), new List <string>())
                })).ToList()));
            });

            A.CallTo(() => _asInfoProvider.GetAsInfo(A <List <string> > ._)).ReturnsLazily((List <string> arguments) =>
            {
                return(Task.FromResult(arguments.Select(x => new AsInfo {
                    IpAddress = x, Description = Guid.NewGuid().ToString()
                }).ToList()));
            });

            A.CallTo(() => _clock.GetDateTimeUtc()).Returns(new DateTime(1999, 01, 01));
        }
Example #6
0
        private async Task HandleReadyToPoll(string hostName, string messageType)
        {
            TlsEntityState state = await LoadState(hostName, messageType);

            if (state.LastUpdated == null || _clock.GetDateTimeUtc() > state.LastUpdated.Value.AddSeconds(_tlsEntityConfig.TlsResultsCacheInSeconds))
            {
                state.TlsState = TlsState.PollPending;

                await _dao.Save(state);

                _dispatcher.Dispatch(new TlsTestPending(hostName), _tlsEntityConfig.SnsTopicArn);
                _log.LogInformation($"A TlsTestPending message for host: {hostName} has been dispatched to SnsTopic: {_tlsEntityConfig.SnsTopicArn}");
            }
            else
            {
                _log.LogInformation($"A request to re-test {hostName} was ignored as it was last tested at {state.LastUpdated.Value} and the re-test cache " +
                                    $"is {TimeSpan.FromSeconds(_tlsEntityConfig.TlsResultsCacheInSeconds):dd\\d\\:hh\\h\\:mm\\m\\:ss\\s}."); // dd/hh/mm/ss
            }
        }
        public async Task HandleCreateDomainPublishesIfStale()
        {
            A.CallTo(() => _clock.GetDateTimeUtc()).Returns(DateTime.UnixEpoch);
            A.CallTo(() => _dkimEntityDao.Get(Id)).Returns(new DkimEntityState(Id, 1, DkimState.Created, DateTime.UnixEpoch, DateTime.UnixEpoch.AddDays(-2), DateTime.UnixEpoch, new List <DkimSelector>()));

            await _dkimEntity.Handle(new DomainCreated(Id, "*****@*****.**", DateTime.Now));

            A.CallTo(() => _dkimEntityDao.Save(A <DkimEntityState> ._)).MustNotHaveHappened();
            A.CallTo(() => _dispatcher.Dispatch(A <DkimEntityCreated> .That.Matches(x => x.State == DkimState.Created && x.Id == Id), A <string> ._)).MustHaveHappenedOnceExactly();
        }
Example #8
0
        public async Task GetCertificatesGoesToOriginForStateOnFirstCallAndReturnsValue()
        {
            A.CallTo(() => _clock.GetDateTimeUtc()).Returns(new DateTime(2018, 01, 01));
            string issuer = "CN=ABC, O=ABC, S=LONDON, C=uk";

            X509Certificate x509Certificate = A.Fake <X509Certificate>();

            A.CallTo(() => x509Certificate.Issuer).Returns(issuer);
            A.CallTo(() => x509Certificate.Subject).Returns(issuer);

            A.CallTo(() => _rootCertificateProvider.GetRootCaCertificates())
            .Returns(Task.FromResult(new List <X509Certificate> {
                x509Certificate
            }));

            X509Certificate certificate = await _rootCertificateLookUp.GetCertificate(issuer);

            Assert.That(certificate, Is.Not.Null);
            A.CallTo(() => _rootCertificateProvider.GetRootCaCertificates()).MustHaveHappenedOnceExactly();
        }
Example #9
0
        public async Task <X509Certificate> GetCertificate(string issuer)
        {
            DateTime now = _clock.GetDateTimeUtc();

            if (_certificateDictionary == null || _certificateDictionaryCacheExpiryTime <= now)
            {
                List <X509Certificate> x509Certificates = await _rootCertificateProvider.GetRootCaCertificates();

                _certificateDictionary = x509Certificates.Where(_ => _.Issuer.Trim() == _.Subject.Trim()).ToDictionary(_ => _.Issuer.Trim().ToLower());
                _certificateDictionaryCacheExpiryTime = now.Add(_certificateDictionaryTtl);
            }

            _certificateDictionary.TryGetValue(issuer.Trim().ToLower(), out var cert);
            return(cert);
        }
        public async Task <List <IpAddressDetails> > Lookup(List <IpAddressDetailsRequest> ipAddressDetailsRequests)
        {
            DateTime nowUtc = _clock.GetDateTimeUtc();

            List <string> distinctIpAddresses = ipAddressDetailsRequests
                                                .Select(x => x.IpAddress).Distinct().ToList();

            List <string> blockListIpAddressesToLookUp = ipAddressDetailsRequests
                                                         .Where(x => x.Date >= nowUtc.Date.AddDays(BlocklistLookupWindowDays))
                                                         .Select(x => x.IpAddress).Distinct().ToList();

            _log.LogDebug($"IpAddressLookup received request for {ipAddressDetailsRequests.Count} IpAddressDetails containing {distinctIpAddresses.Count} distinct IP addresses and {blockListIpAddressesToLookUp.Count} distinct IP addresses within blocklist window");

            Task <List <BlocklistResult> >  blocklistResultsTask    = _blocklistProvider.GetBlocklistAppearances(blockListIpAddressesToLookUp);
            Task <List <AsInfo> >           asInfoTask              = _asInfoProvider.GetAsInfo(distinctIpAddresses);
            Task <List <ReverseDnsResult> > reverseDnsResponsesTask = _reverseDnsProvider.GetReverseDnsResult(distinctIpAddresses);

            await Task.WhenAll(blocklistResultsTask, asInfoTask, reverseDnsResponsesTask);

            List <BlocklistResult>  blocklistResults    = await blocklistResultsTask;
            List <AsInfo>           asInfos             = await asInfoTask;
            List <ReverseDnsResult> reverseDnsResponses = await reverseDnsResponsesTask;

            Dictionary <string, BlocklistResult>  blocklistResultsDictionary   = blocklistResults.Where(x => x.IpAddress != null).ToDictionary(x => x.IpAddress);
            Dictionary <string, AsInfo>           asInfoDictionary             = asInfos.Where(x => x.IpAddress != null).ToDictionary(x => x.IpAddress);
            Dictionary <string, ReverseDnsResult> reverseDnsResponseDictionary = reverseDnsResponses.Where(x => x.OriginalIpAddress != null).ToDictionary(x => x.OriginalIpAddress);

            List <IpAddressDetails> responses = ipAddressDetailsRequests.Select(request =>
            {
                bool blockListResultHasValue    = blocklistResultsDictionary.TryGetValue(request.IpAddress, out BlocklistResult blockListResult);
                bool asInfoHasValue             = asInfoDictionary.TryGetValue(request.IpAddress, out AsInfo asInfo);
                bool reverseDnsResponseHasValue = reverseDnsResponseDictionary.TryGetValue(request.IpAddress, out ReverseDnsResult reverseDnsResult);
                return(new IpAddressDetails(
                           request.IpAddress,
                           request.Date,
                           asInfoHasValue ? asInfo.AsNumber : 0,
                           asInfoHasValue ? asInfo.Description : "",
                           asInfoHasValue ? asInfo.CountryCode : "",
                           blockListResultHasValue ? blockListResult.BlocklistAppearances : null,
                           reverseDnsResponseHasValue ? reverseDnsResult.ForwardResponses : null,
                           nowUtc.AddDays(AsnDataAgeDays).Date,
                           nowUtc,
                           nowUtc));
            }).ToList();

            _log.LogInformation($"Returning {responses.Count} IpAddressDetails from request for {ipAddressDetailsRequests.Count}");
            return(responses);
        }
        public async Task ShouldUpdateAndDispatchPollPendingWhenScheduledReminderArrivesAfterTtl()
        {
            const string hostName    = "testhostname";
            const string snsTopicArn = "snsTopicArn";
            DateTime     testDate    = new DateTime(2000, 01, 01);

            A.CallTo(() => _clock.GetDateTimeUtc()).Returns(testDate);
            A.CallTo(() => _dao.Get(hostName)).Returns(new TlsEntityState(hostName)
            {
                TlsState = TlsState.Created, LastUpdated = testDate - TimeSpan.FromSeconds(3)
            });
            A.CallTo(() => _tlsEntityConfig.TlsResultsCacheInSeconds).Returns(2);
            A.CallTo(() => _tlsEntityConfig.SnsTopicArn).Returns(snsTopicArn);

            await _tlsEntity.Handle(new TlsScheduledReminder(Guid.NewGuid().ToString(), hostName));

            A.CallTo(() => _dao.Save(A <TlsEntityState> .That.Matches(e => e.TlsState == TlsState.PollPending))).MustHaveHappenedOnceExactly();
            A.CallTo(() => _dispatcher.Dispatch(A <TlsTestPending> .That.Matches(entity => entity.Id == hostName), snsTopicArn)).MustHaveHappenedOnceExactly();
        }
        public async Task Handle(DomainCreated message)
        {
            string domainName = message.Id.ToLower();

            MxEntityState state = await _dao.Get(domainName);

            if (state != null)
            {
                _log.LogInformation($"Ignoring {nameof(DomainCreated)} as MxEntity already exists for {domainName}.");
                return;
            }

            await _dao.Save(new MxEntityState(domainName));

            _log.LogInformation($"Created MxEntity for {domainName}.");

            MxEntityCreated mxEntityCreated = new MxEntityCreated(domainName);

            _dispatcher.Dispatch(mxEntityCreated, _mxEntityConfig.SnsTopicArn);
            _log.LogInformation(
                $"An MxEntityCreated message for Domain: {domainName} has been dispatched to SnsTopic: {_mxEntityConfig.SnsTopicArn}");

            Message createScheduledReminder = new CreateScheduledReminder(Guid.NewGuid().ToString(), "Mx", domainName, _clock.GetDateTimeUtc());

            _dispatcher.Dispatch(createScheduledReminder, _mxEntityConfig.SnsTopicArn);
            _log.LogInformation(
                $"A CreateScheduledReminder message for Domain: {domainName} has been dispatched to SnsTopic: {_mxEntityConfig.SnsTopicArn}");
        }
        public async Task Handle(DomainCreated message)
        {
            string id = message.Id.ToLower();

            DkimEntityState state = await _dao.Get(id);

            if (state != null)
            {
                if (state.RecordsLastUpdated == null || state.RecordsLastUpdated.Value.AddDays(DaysBeforeBeingConsideredStale) <= _clock.GetDateTimeUtc())
                {
                    _dispatcher.Dispatch(new DkimEntityCreated(id, state.Version), _config.SnsTopicArn);
                    _log.LogInformation("Published DkimEntityCreated for stale DkimEntity {Id}.", id);
                }
                else
                {
                    throw new MailCheckException($"Cannot handle event {nameof(DomainCreated)} as an up to date DkimEntity already exists for {id}.");
                }
            }
            else
            {
                state = new DkimEntityState(id, 1, DkimState.Created, DateTime.UtcNow, null, null, null);
                await _dao.Save(state);

                _dispatcher.Dispatch(new DkimEntityCreated(id, state.Version), _config.SnsTopicArn);
                _log.LogInformation("Created DkimEntity for {Id}.", id);
            }
        }
        public async Task <EntityDkimEntityState> GetDkimForDomain(string requestDomain)
        {
            EntityDkimEntityState response = await _dao.GetDkimSelectors(requestDomain);

            if (response?.RecordsLastUpdated == null || response.RecordsLastUpdated.Value.AddDays(DaysBeforeBeingConsideredStale) <= _clock.GetDateTimeUtc())
            {
                await _messagePublisher.Publish(new DomainMissing(requestDomain), _config.MicroserviceOutputSnsTopicArn);
            }

            return(response);
        }