示例#1
0
        public override async Task UserStateChanged(Murmur.User user)
        {
            using (var context = await FancyContext.Connect())
                using (var transact = context.Database.BeginTransaction())
                {
                    var current = await context.Logs.Where(x => x.Who.Id == user.userid)
                                  .OrderByDescending(x => x.When).Select(x => x.Where.Id).FirstAsync();

                    if (current != user.channel)
                    {
                        context.Logs.Add(new LogEntry.ChannelSwitched
                        {
                            When = DateTimeOffset.Now,
                            Who  = context.Users.Attach(new User {
                                Id = user.userid
                            }),
                            Where = await context.Channels.SingleAsync(x => x.ServerId == user.channel),
                        });

                        await context.SaveChangesAsync();
                    }

                    transact.Commit();
                }
        }
示例#2
0
        public override async Task <Wrapped.AuthenticatorUpdateResult> SetInfo(int id, Dictionary <Murmur.UserInfo, string> info)
        {
            using (var context = await FancyContext.Connect())
                using (var transact = context.Database.BeginTransaction(IsolationLevel.Serializable)) {
                    var user = await context.Users.Include(x => x.Membership).SingleAsync(x => x.Id == id);

                    foreach (var kv in info)
                    {
                        switch (kv.Key)
                        {
                        case Murmur.UserInfo.UserComment:
                            user.Membership.Comment = kv.Value;
                            break;

                        default:
                            System.Diagnostics.Trace.WriteLine(kv.Key, "Unhandled thing in SetInfo");
                            transact.Rollback();
                            return(Wrapped.AuthenticatorUpdateResult.Failure);
                        }
                    }

                    await context.SaveChangesAsync();

                    transact.Commit();
                    return(Wrapped.AuthenticatorUpdateResult.Success);
                }
        }
示例#3
0
        public override async Task UserConnected(Murmur.User user)
        {
            using (var context = await FancyContext.Connect())
                using (var transact = context.Database.BeginTransaction(IsolationLevel.Serializable))
                {
                    var userNotificationsQuery = from usr in context.Users.Include(x => x.PersistentGuest.Godfathers)
                                                 .Include(x => x.GuestInvite.Inviter).Include(x => x.Membership)
                                                 where usr.Id == user.userid
                                                 join evt in context.Logs.OfType <LogEntry.Connected>() on usr.Id equals evt.Who.Id into connectedEvents
                                                 let lastConnection = connectedEvents.Max(x => x.When)
                                                                      join notific in context.OfflineNotifications on usr.Id equals notific.Recipient.Id into notifications
                                                                      select new { usr, notifications = notifications.Where(x => x.When > lastConnection) };
                    var res = await userNotificationsQuery.SingleAsync();

                    foreach (var notify in res.notifications)
                    {
                        await Server.SendMessage(user.session, notify.Message);
                    }

                    if (res.usr.Membership == null)
                    {
                        var onlineUsers = await Server.GetUsers();

                        var godfathers = res.usr.PersistentGuest?.Godfathers?.Select(x => x.UserId) ?? new[] { res.usr.GuestInvite.Inviter.Id };
                        if (!godfathers.Intersect(onlineUsers.Select(x => x.Value.userid)).Any())
                        {
                            await Server.KickUser(user.session, "Inviter not online.");

                            return;
                        }

                        if (res.usr.GuestInvite != null)
                        {
                            // move guest to inviter
                            var inviter = onlineUsers.Single(x => x.Value.userid == res.usr.GuestInvite.InviterId);
                            user.channel  = inviter.Value.channel;
                            user.suppress = false;
                            await Server.SetState(user);
                        }
                    }

                    context.Logs.Add(new LogEntry.Connected
                    {
                        When  = DateTimeOffset.Now,
                        Who   = res.usr,
                        Where = await context.Channels.SingleAsync(x => x.ServerId == user.channel),
                    });
                    await context.SaveChangesAsync();

                    transact.Commit();
                }
        }
示例#4
0
        public override async Task UserTextMessage(Murmur.User user, Murmur.TextMessage message)
        {
            using (var context = await FancyContext.Connect())
                using (var transact = context.Database.BeginTransaction())
                {
                    User senderEntity = null;
                    if (user.userid > 0)
                    {
                        senderEntity = await context.Users.FindAsync(user.userid);
                    }

                    var qtext = message.text.Replace("&quot;", "\"");
                    var msg   = CommandPattern.Matches(qtext).Cast <Match>().Select(m => m.Value).ToArray();
                    if (message.channels.Any())
                    {
                        if (msg[0] == "@fancy-ng")
                        {
                            await CommandMgr.HandleCommand(SteamListener, Server, user, msg.Skip(1));
                        }

                        if (senderEntity != null)
                        {
                            context.Logs.Add(new LogEntry.ChatMessage
                            {
                                When    = DateTimeOffset.Now,
                                Who     = senderEntity,
                                Where   = await context.Channels.SingleAsync(x => x.ServerId == user.channel),
                                Message = message.text
                            });
                        }
                    }

                    if (senderEntity != null)
                    {
                        var messagesInTheLastSeconds = await context.Logs.OfType <LogEntry.ChatMessage>()
                                                       .Where(x => x.Who.Id == senderEntity.Id && x.When > DbFunctions.AddSeconds(DateTimeOffset.Now, -5)).CountAsync();

                        if (messagesInTheLastSeconds >= 3)
                        {
                            await Server.KickUser(user.session, "Who are you, my evil twin?! [stop spamming]");
                        }
                    }

                    await context.SaveChangesAsync();

                    transact.Commit();
                }
        }
示例#5
0
        public override async Task ChannelRemoved(Murmur.Channel chan)
        {
            using (var context = await FancyContext.Connect())
            {
                var channel = await context.Channels.Where(x => x.ServerId == chan.id).SingleAsync();

                channel.ServerId = null;

                context.ChannelInfoChanges.Add(new Channel.InfoChange
                {
                    Channel     = channel,
                    Name        = null,
                    Description = null,
                    When        = DateTimeOffset.Now,
                });

                await context.SaveChangesAsync();
            }
        }
示例#6
0
        public override async Task <Wrapped.AuthenticatorUpdateResult> SetTexture(int id, byte[] texture)
        {
            using (var context = await FancyContext.Connect())
                using (var transact = context.Database.BeginTransaction(IsolationLevel.Serializable)) {
                    var entity = new Membership {
                        UserId = id, Texture = texture
                    };
                    context.Memberships.Attach(entity);
                    context.Entry(entity).Property(x => x.Texture).IsModified = true;
                    context.Configuration.ValidateOnSaveEnabled = false;
                    try {
                        await context.SaveChangesAsync();
                    } catch (Exception) {
                        return(Wrapped.AuthenticatorUpdateResult.Failure);
                    }

                    transact.Commit();
                    return(Wrapped.AuthenticatorUpdateResult.Success);
                }
        }
示例#7
0
        public override async Task UserDisconnected(Murmur.User user)
        {
            Model.UserAttribute.CertificateCredentials cc;
            Fancyauth.GuestCredentials.TryRemove(user.userid, out cc);

            using (var context = await FancyContext.Connect())
                using (var transact = context.Database.BeginTransaction())
                {
                    context.Logs.Add(new LogEntry.Disconnected
                    {
                        When = DateTimeOffset.Now,
                        Who  = context.Users.Attach(new User {
                            Id = user.userid
                        }),
                        Where = await context.Channels.SingleAsync(x => x.ServerId == user.channel),
                    });

                    await context.SaveChangesAsync();

                    transact.Commit();
                }
        }
示例#8
0
        public override async Task ChannelStateChanged(Murmur.Channel chan)
        {
            using (var context = await FancyContext.Connect())
            {
                var query = from channel in context.Channels
                            where channel.ServerId == chan.id
                            join ichange in context.ChannelInfoChanges on channel.Id equals ichange.Channel.Id into infoChanges
                            select new
                {
                    channel,
                    parentId = channel.Parent.ServerId,
                    name     = infoChanges.OrderByDescending(x => x.When).Select(x => x.Name).Where(x => x != null).FirstOrDefault(),
                    desc     = infoChanges.OrderByDescending(x => x.When).Select(x => x.Description).Where(x => x != null).FirstOrDefault(),
                };
                var res = await query.SingleAsync();

                var infoChange = new Channel.InfoChange
                {
                    Channel     = res.channel,
                    Name        = chan.name == res.name ? null : chan.name,
                    Description = chan.description == res.desc ? null : chan.description,
                    When        = DateTimeOffset.Now,
                };

                if (res.parentId != chan.parent)
                {
                    res.channel.Parent = await context.Channels.Where(x => x.ServerId == chan.parent).SingleAsync();
                }

                if (infoChange.Name != null || infoChange.Description != null)
                {
                    context.ChannelInfoChanges.Add(infoChange);
                }

                await context.SaveChangesAsync();
            }
        }
示例#9
0
        public override async Task ChannelCreated(Murmur.Channel chan)
        {
            using (var context = await FancyContext.Connect())
                using (var transact = context.Database.BeginTransaction())
                {
                    var dbchan = context.Channels.Add(new Channel
                    {
                        Temporary = chan.temporary,
                        Parent    = await context.Channels.Where(x => x.ServerId == chan.parent).SingleAsync(),
                        ServerId  = chan.id,
                    });
                    context.ChannelInfoChanges.Add(new Channel.InfoChange
                    {
                        Channel     = dbchan,
                        Name        = chan.name,
                        Description = chan.description,
                        When        = DateTimeOffset.Now
                    });

                    await context.SaveChangesAsync();

                    transact.Commit();
                }
        }
示例#10
0
        public override async Task <Wrapped.AuthenticationResult> Authenticate(string name, string pw, byte[][] certificates, string certhash, bool certstrong)
        {
            string fingerprint = null;
            long?  certSerial  = null;
            string subCN       = null;

            Org.BouncyCastle.X509.X509Certificate bouncyCert = null;
            if (certificates.Length > 0)
            {
                var certs    = certificates.Select(x => new X509Certificate2(x)).ToArray();
                var usercert = certs.Last();
                var chain    = new X509Chain();
                foreach (var cert in certs)
                {
                    chain.ChainPolicy.ExtraStore.Add(cert);
                }
                chain.ChainPolicy.RevocationMode    = X509RevocationMode.NoCheck;
                chain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority;
                chain.Build(usercert);

                bouncyCert = new X509CertificateParser().ReadCertificate(certificates.Last());
                var subDN = bouncyCert.SubjectDN;
                subCN      = subDN.GetValueList()[subDN.GetOidList().IndexOf(X509ObjectIdentifiers.CommonName)] as string;
                certSerial = bouncyCert.SerialNumber.LongValue;

                fingerprint = usercert.Thumbprint;
            }
            else
            {
                // oh @moritzuehling y u do dis to me Q_Q
                // (no certs at all)
            }

            CertificateCredentials creds = null;

            if (certSerial.HasValue)
            {
                creds = new CertificateCredentials
                {
                    Fingerprint = fingerprint,
                    CertSerial  = certSerial.Value,
                }
            }
            ;


            User user;
            bool isGuest = false;

            using (var context = await FancyContext.Connect())
                using (var transact = context.Database.BeginTransaction(IsolationLevel.Serializable)) {
                    user = await context.Users.Include(x => x.Membership).Include(x => x.PersistentGuest)
                           .Include(x => x.PersistentGuest.Godfathers).SingleOrDefaultAsync(x => x.CertCredentials.Any(y => y.Fingerprint == fingerprint));

                    if (user != null)
                    {
                        // Known user. Why?
                        // * member
                        // * persistent guest
                        //
                        // As this is the /authenticator/, we can't query online users because that would deadlock murmur's main thread.
                        // As someone with CertificateCredentials is definitely allowed to connect, we just let them pass here and
                        // kick them in OnUserConnected if they're missing a godfather.
                    }
                    else
                    {
                        // Unknown user. Why?
                        // * temporary guest
                        // * new cert for existing user
                        // * new user
                        // * random person on the internet

                        // Let's first check for guest invites
                        pw = pw.Trim();
                        var invite = await context.Invites.SingleOrDefaultAsync(x => (x.Code == pw) && (x.ExpirationDate > DateTimeOffset.Now));

                        if (invite != null)
                        {
                            // Try to match by name.
                            user = await context.Users.Include(x => x.PersistentGuest).SingleOrDefaultAsync(x => x.Name == name);

                            if (user != null)
                            {
                                if (user.GuestInvite == null)
                                {
                                    // In case the name is already taken by a non-guest, we force them to a
                                    // random but different name so everyone can see they're a guest.
                                    name += "-guest-" + Guid.NewGuid().ToString();
                                    user  = null;
                                }
                                else
                                {
                                    // The account once belonged to a guest? Nice.
                                    // But adjust to the new guest invite.
                                    user.GuestInvite = invite;
                                    // (Note that we don't care about the edge case where guests get ghost-kicked
                                    //  by other guests because we simply don't care about guests.)
                                }
                            }

                            if (user == null)
                            {
                                // Create a new user for this name if we need to.
                                user = context.Users.Add(new User
                                {
                                    Name        = name,
                                    GuestInvite = invite,
                                });
                            }

                            isGuest = true;
                        }
                        else
                        {
                            // random person on the internet; has no signed or valid certificate
                            if (!certstrong)
                            {
                                return(Wrapped.AuthenticationResult.Forbidden());
                            }

                            // New cert for existing user?

                            /*
                             * not longer supported
                             * foreach (System.Collections.ICollection sans in bouncyCert.GetSubjectAlternativeNames())
                             * {
                             *  var enm = sans.GetEnumerator();
                             *  enm.MoveNext();
                             *  enm.MoveNext();
                             *  var val = enm.Current as string;
                             *  var match = Regex.Match(val ?? String.Empty, "^([^@]*)@user.mumble.ehvag.de$");
                             *  if (match.Success)
                             *  {
                             *      var oldName = match.Groups[1].Captures[0].Value;
                             *      var existingUser = await context.Users.Include(x => x.CertCredentials).SingleOrDefaultAsync(x => x.Name == oldName && x.CertCredentials.CertSerial < certSerial);
                             *      if (existingUser != null)
                             *      {
                             *          existingUser.Name = subCN;
                             *          existingUser.CertCredentials.CertSerial = certSerial.Value;
                             *          existingUser.CertCredentials.Fingerprint = fingerprint;
                             *          user = existingUser;
                             *          break;
                             *      }
                             *  }
                             * }
                             */

                            if (user == null)
                            {
                                // no existing user found, so create new user
                                user = context.Users.Add(new User
                                {
                                    Name            = subCN,
                                    CertCredentials = new List <CertificateCredentials> {
                                        creds
                                    },
                                    Membership = new Membership()
                                });
                            }
                        }
                    }

                    // As stated above, we can't query mumble for connected users to reject persistent guests.
                    // However, we can use the logs in the database as a heuristic to get currently connected users.
                    if (user.PersistentGuest != null && !isGuest)
                    {
                        var godfathersQuery = from usr in context.Users
                                              from godfathership in usr.Membership.Godfatherships
                                              where godfathership.UserId == user.Id
                                              join e in context.Logs.OfType <LogEntry.Connected>() on usr.Id equals e.Who.Id into connectEvents
                                              join e in context.Logs.OfType <LogEntry.Disconnected>() on usr.Id equals e.Who.Id into disconnectEvents
                                              select new { Con = connectEvents.Max(x => x.When), Dis = disconnectEvents.Max(x => x.When) };
                        var godfatherConnected = await godfathersQuery.AnyAsync(l => l.Con > l.Dis);

                        if (!godfatherConnected)
                        {
                            return(Wrapped.AuthenticationResult.Forbidden());
                        }
                    }

                    await context.SaveChangesAsync();

                    transact.Commit();
                }

            if (isGuest && (creds != null))
            {
                Fancyauth.GuestCredentials.AddOrUpdate(user.Id, creds, (k, c) => creds);
            }

            string[] groups = null;
            if (user.PersistentGuest != null)
            {
                groups = PersistentGuestGroups;
            }

            return(Wrapped.AuthenticationResult.Success(user.Id, user.Name, groups));
        }
示例#11
0
        public async Task UpdateChannelModel(Server server)
        {
            var tree = await server.GetTree();

            using (var context = await FancyContext.Connect())
                using (var transact = context.Database.BeginTransaction())
                {
                    var allChanQuery = from channel in context.Channels
                                       join ichange in context.ChannelInfoChanges on channel.Id equals ichange.Channel.Id into infoChanges
                                       select new
                    {
                        channel,
                        name = infoChanges.OrderByDescending(x => x.When).Select(x => x.Name).Where(x => x != null).FirstOrDefault(),
                        desc = infoChanges.OrderByDescending(x => x.When).Select(x => x.Description).Where(x => x != null).FirstOrDefault(),
                    };
                    var allChans = await allChanQuery.ToArrayAsync();

                    var hitChans = new List <Channel>();
                    var treewalk = new Queue <Murmur.Tree>();
                    treewalk.Enqueue(tree);
                    while (treewalk.Any())
                    {
                        var current = treewalk.Dequeue();

                        var dbChanBig = allChans.Where(x => x.channel.ServerId == current.c.id).SingleOrDefault();
                        var dbChan    = dbChanBig == null ? null : dbChanBig.channel;
                        if (dbChan == null)
                        {
                            dbChan = context.Channels.Add(new Channel
                            {
                                Temporary = current.c.temporary,
                                Parent    = current.c.parent == -1 ? null : hitChans.Where(x => x.ServerId == current.c.parent).Single(),
                                ServerId  = current.c.id,
                            });
                            context.ChannelInfoChanges.Add(new Channel.InfoChange
                            {
                                Channel     = dbChan,
                                Name        = current.c.name,
                                Description = current.c.description,
                                When        = DateTimeOffset.Now
                            });
                        }
                        else if ((dbChanBig.name != current.c.name) || (dbChanBig.desc != current.c.description))
                        {
                            // existing, but modified
                            context.ChannelInfoChanges.Add(new Channel.InfoChange
                            {
                                Channel     = dbChan,
                                Name        = current.c.name == dbChanBig.name ? null : current.c.name,
                                Description = current.c.description == dbChanBig.desc ? null : current.c.description,
                                When        = DateTimeOffset.Now,
                            });
                        }

                        hitChans.Add(dbChan);

                        foreach (var child in current.children)
                        {
                            treewalk.Enqueue(child);
                        }
                    }

                    foreach (var channel in allChans.Select(x => x.channel).Except(hitChans))
                    {
                        channel.ServerId = null;

                        context.ChannelInfoChanges.Add(new Channel.InfoChange
                        {
                            Channel     = channel,
                            Name        = null,
                            Description = null,
                            When        = DateTimeOffset.Now,
                        });
                    }

                    await context.SaveChangesAsync();

                    transact.Commit();
                }
        }