public async Task PrepareTest()
        {
            serviceProvider = FakeServiceProvider.Create($"{nameof(DeliveryServiceTests)}_{TestContext.TestName}");
            scope           = serviceProvider.CreateScope();
            connections     = scope.ServiceProvider.GetRequiredService <ConnectionsService>();
            packets         = scope.ServiceProvider.GetRequiredService <PacketService>();
            database        = scope.ServiceProvider.GetRequiredService <DatabaseContext>();
            delivery        = scope.ServiceProvider.GetRequiredService <DeliveryService>();

            database.Accounts.Add(new Account {
                AccountId = alice
            });
            database.MailConfirmations.Add(new MailConfirmation {
                AccountId = alice, MailAddress = "*****@*****.**", Token = SkynetRandom.String(10)
            });
            database.Accounts.Add(new Account {
                AccountId = bob
            });
            database.MailConfirmations.Add(new MailConfirmation {
                AccountId = bob, MailAddress = "*****@*****.**", Token = SkynetRandom.String(10)
            });
            database.Accounts.Add(new Account {
                AccountId = charlie
            });
            database.MailConfirmations.Add(new MailConfirmation {
                AccountId = charlie, MailAddress = "*****@*****.**", Token = SkynetRandom.String(10)
            });
            await database.SaveChangesAsync().ConfigureAwait(false);
        }
        public async Task TestAddAccountAndSession()
        {
            await AsyncParallel.ForAsync(0, 100, async i =>
            {
                using IServiceScope scope = serviceProvider.CreateScope();
                var database = scope.ServiceProvider.GetRequiredService <DatabaseContext>();

                (var account, _, bool success) = await database
                                                 .AddAccount($"{SkynetRandom.String(10)}@example.com", Array.Empty <byte>()).ConfigureAwait(false);
                Assert.IsTrue(success);

                await AsyncParallel.ForAsync(0, 10, async j =>
                {
                    using IServiceScope scope = serviceProvider.CreateScope();
                    var database = scope.ServiceProvider.GetRequiredService <DatabaseContext>();

                    Session session = new Session()
                    {
                        AccountId             = account.AccountId,
                        SessionTokenHash      = SkynetRandom.Bytes(32), // No real hashes needed here
                        WebTokenHash          = SkynetRandom.Bytes(32),
                        ApplicationIdentifier = "windows/SkynetServer.Database.Tests"
                    };
                    await database.AddSession(session).ConfigureAwait(false);
                }).ConfigureAwait(false);
            }).ConfigureAwait(false);
        }
 public async Task TestAddAccount()
 {
     await AsyncParallel.ForAsync(0, 500, async i =>
     {
         using IServiceScope scope = serviceProvider.CreateScope();
         var database         = scope.ServiceProvider.GetRequiredService <DatabaseContext>();
         (_, _, bool success) = await database.AddAccount($"{SkynetRandom.String(10)}@example.com", Array.Empty <byte>()).ConfigureAwait(false);
         Assert.IsTrue(success);
     }).ConfigureAwait(false);
 }
        public async Task TestAddChannelWithOwner()
        {
            await AsyncParallel.ForAsync(0, 50, async i =>
            {
                using IServiceScope scope = serviceProvider.CreateScope();
                var database = scope.ServiceProvider.GetRequiredService <DatabaseContext>();

                (var account, _, bool success) = await database
                                                 .AddAccount($"{SkynetRandom.String(10)}@example.com", Array.Empty <byte>()).ConfigureAwait(false);
                Assert.IsTrue(success);

                Channel channel = new Channel()
                {
                    OwnerId     = account.AccountId,
                    ChannelType = ChannelType.Loopback
                };
                await database.AddChannel(channel).ConfigureAwait(false);
            }).ConfigureAwait(false);
        }
Exemple #5
0
        public async Task <(Account, MailConfirmation, bool)> AddAccount(string mailAddress, byte[] passwordHash)
        {
            Account account = new Account {
                PasswordHash = passwordHash
            };
            MailConfirmation confirmation = new MailConfirmation {
                Account = account, MailAddress = mailAddress
            };

            bool saved = false;

            do
            {
                try
                {
                    long   id    = SkynetRandom.Id();
                    string token = SkynetRandom.String(10);
                    account.AccountId  = id;
                    confirmation.Token = token;
                    Accounts.Add(account);
                    MailConfirmations.Add(confirmation);
                    await SaveChangesAsync().ConfigureAwait(false);

                    saved = true;
                }
                catch (DbUpdateException ex) when(ex?.InnerException is MySqlException mex && mex.Number == 1062)
                {
                    // Return false if unique constraint violation is caused by the mail address
                    // An example for mex.Message is "Duplicate entry '*****@*****.**' for key 'PRIMARY'"

                    if (mex.Message.Contains('@', StringComparison.Ordinal))
                    {
                        return(null, null, false);
                    }
                }
            } while (!saved);
            return(account, confirmation, true);
        }
        public override async ValueTask Handle(P06CreateSession packet)
        {
            var response = Packets.New <P07CreateSessionResponse>();

            using var csp = SHA256.Create();
            byte[] passwordHash = csp.ComputeHash(packet.KeyHash);

            // As of RFC 5321 the local-part of an email address should not be case-sensitive.
            // EF Core converts the C# == operator to = in SQL which compares the contents of byte arrays
            var confirmation = await
                                   (from c in Database.MailConfirmations.AsQueryable().Where(c => c.MailAddress == packet.AccountName.ToLowerInvariant())
                                   join a in Database.Accounts.AsQueryable().Where(a => a.PasswordHash == passwordHash)
                                   on c.AccountId equals a.AccountId
                                   select c)
                               .SingleOrDefaultAsync().ConfigureAwait(false);

            if (confirmation == null)
            {
                response.StatusCode = CreateSessionStatus.InvalidCredentials;
                await Client.Send(response).ConfigureAwait(false);

                return;
            }
            if (confirmation.ConfirmationTime == default)
            {
                response.StatusCode = CreateSessionStatus.UnconfirmedAccount;
                await Client.Send(response).ConfigureAwait(false);

                return;
            }

            byte[] sessionToken     = SkynetRandom.Bytes(32);
            byte[] sessionTokenHash = csp.ComputeHash(sessionToken);
            string webToken         = SkynetRandom.String(30);

            byte[] webTokenHash = csp.ComputeHash(Encoding.UTF8.GetBytes(webToken));

            Session session = await Database.AddSession(new Session
            {
                AccountId             = confirmation.AccountId,
                SessionTokenHash      = sessionTokenHash,
                WebTokenHash          = webTokenHash,
                ApplicationIdentifier = Client.ApplicationIdentifier,
                LastConnected         = DateTime.Now,
                LastVersionCode       = Client.VersionCode,
                FcmToken = packet.FcmRegistrationToken
            }).ConfigureAwait(false);

            Message deviceList = await injector.CreateDeviceList(confirmation.AccountId).ConfigureAwait(false);

            Client.Authenticate(confirmation.AccountId, session.SessionId);

            response.StatusCode   = CreateSessionStatus.Success;
            response.AccountId    = session.AccountId;
            response.SessionId    = session.SessionId;
            response.SessionToken = sessionToken;
            response.WebToken     = webToken;
            await Client.Send(response).ConfigureAwait(false);

            await Delivery.StartSyncChannels(Client, new List <long>(), lastMessageId : default).ConfigureAwait(false);

            await Delivery.StartSendMessage(deviceList, null).ConfigureAwait(false);

            IClient old = connections.Add(Client);

            if (old != null)
            {
                _ = old.DisposeAsync(unregister: false);
            }
        }
            private async Task OnExecute(IConsole console, IServiceProvider provider)
            {
                long      accountId, channelId;
                Stopwatch stopwatch = new Stopwatch();

                using (IServiceScope scope = provider.CreateScope())
                {
                    DatabaseContext database = scope.ServiceProvider.GetRequiredService <DatabaseContext>();
                    (var account, var confirmation, bool success) = await database
                                                                    .AddAccount($"{SkynetRandom.String(10)}@example.com", Array.Empty <byte>()).ConfigureAwait(false);

                    accountId = account.AccountId;
                    console.Out.WriteLine($"Created account {confirmation.MailAddress} with ID {accountId}");
                    console.Out.WriteLine($"Created mail confirmation for {confirmation.MailAddress} with token {confirmation.Token}");
                }

                using (IServiceScope scope = provider.CreateScope())
                {
                    DatabaseContext database = scope.ServiceProvider.GetRequiredService <DatabaseContext>();
                    Channel         channel  = await database.AddChannel(new Channel()
                    {
                        OwnerId = accountId
                    }).ConfigureAwait(false);

                    channelId = channel.ChannelId;
                    console.Out.WriteLine($"Created channel {channelId} with owner {accountId}");
                }

                if (AccountCount > 0)
                {
                    console.Out.WriteLine($"Inserting {AccountCount} accounts...");
                    stopwatch.Start();

                    await AsyncParallel.ForAsync(0, AccountCount, async i =>
                    {
                        using IServiceScope scope = provider.CreateScope();
                        DatabaseContext ctx       = scope.ServiceProvider.GetRequiredService <DatabaseContext>();
                        await ctx.AddAccount($"{SkynetRandom.String(10)}@example.com", Array.Empty <byte>()).ConfigureAwait(false);
                    }).ConfigureAwait(false);

                    stopwatch.Stop();
                    console.Out.WriteLine($"Finished saving {AccountCount} accounts after {stopwatch.ElapsedMilliseconds}ms");
                    stopwatch.Reset();
                }

                if (MessageCount > 0)
                {
                    console.Out.WriteLine($"Inserting {MessageCount} messages to channel {channelId}...");
                    stopwatch.Start();

                    await AsyncParallel.ForAsync(0, MessageCount, async i =>
                    {
                        using IServiceScope scope = provider.CreateScope();
                        DatabaseContext database  = scope.ServiceProvider.GetRequiredService <DatabaseContext>();
                        Message entity            = new Message {
                            ChannelId = channelId, SenderId = accountId
                        };
                        database.Messages.Add(entity);
                        await database.SaveChangesAsync().ConfigureAwait(false);
                    }).ConfigureAwait(false);

                    stopwatch.Stop();
                    console.Out.WriteLine($"Finished saving {MessageCount} messages after {stopwatch.ElapsedMilliseconds}ms");
                    stopwatch.Reset();
                }
            }