private static int? HighestAuthority(AccountModel account, String permission) { return account != null ? account.Group.Permissions.Where(perm => perm.Name == permission).Select(perm => perm.Authority).FirstOrDefault() : null; }
/// <summary> /// Checks if an initiator can act on a command against a target, falling back to a guest authority if either account is missing. /// </summary> /// <param name="initiatorAccount">Who is initiating the action</param> /// <param name="commandName">What action is being taken</param> /// <param name="targetAccount">Who the action is being taken against</param> /// <param name="guestAuthority">The fallback authority to use if neither initiator or target is passed or does not have an authority defined for the command.</param> /// <returns>A command result describing if the action can be taken</returns> private static ICommandResult CheckPermissions(AccountModel initiatorAccount, String commandName, AccountModel targetAccount, int? guestAuthority) { ICommandResult result = null; int? initiatorAuthority = SecurityController.HighestAuthority(initiatorAccount, commandName) ?? guestAuthority; int? targetAuthority = SecurityController.HighestAuthority(targetAccount, commandName) ?? guestAuthority; if (initiatorAuthority.HasValue == true && initiatorAuthority.Value > 0) { if (targetAuthority.HasValue == true && targetAuthority.Value > 0) { if (initiatorAuthority.Value > targetAuthority.Value) { // The initiator "out ranks" the target. Good to go. result = new CommandResult() { Success = true, CommandResultType = CommandResultType.Success }; } else { // The initiator has some permission, but not more than the target. // The cannot execute the command, but we give some further details about it here. result = new CommandResult() { Success = false, CommandResultType = CommandResultType.InsufficientAuthority }; } } else { // The target does not have any permission, so we're good to go. result = new CommandResult() { Success = true, CommandResultType = CommandResultType.Success }; } } else { // The account has zero authority. result = new CommandResult() { Success = false, CommandResultType = CommandResultType.InsufficientPermissions }; } return result; }
/// <summary> /// The underlying check permissions object. /// </summary> /// <param name="command"></param> /// <param name="initiatorAccount"></param> /// <param name="commandName"></param> /// <param name="targetAccount"></param> /// <returns></returns> protected ICommandResult DispatchPermissionsCheck(ICommand command, AccountModel initiatorAccount, String commandName, AccountModel targetAccount = null) { ICommandResult result = null; var guestAuthority = this.Groups.Where(group => group.IsGuest) .SelectMany(group => group.Permissions) .Where(permission => permission.Name == commandName) .Select(permission => permission.Authority) .FirstOrDefault(); if (command.Origin == CommandOrigin.Local) { // All good. result = new CommandResult() { Success = true, CommandResultType = CommandResultType.Success }; } else if (command.Origin == CommandOrigin.Plugin) { if (command.Authentication.Username == null && command.Authentication.Uid == null && command.Authentication.GameType == CommonProtocolType.None) { // The plugin has not provided additional details on who has executed it. result = new CommandResult() { Success = true, CommandResultType = CommandResultType.Success }; } else { // The plugin has supplied us with details on who has initiated the command. result = SecurityController.CheckPermissions(initiatorAccount, commandName, targetAccount, guestAuthority); } } else if (command.Origin == CommandOrigin.Remote) { result = SecurityController.CheckPermissions(initiatorAccount, commandName, targetAccount, guestAuthority); } else { result = new CommandResult() { Success = false, CommandResultType = CommandResultType.InsufficientPermissions }; } return result; }
/// <summary> /// Creates a new account if the specified name is unique. /// </summary> public ICommandResult SecurityGroupAddAccount(ICommand command, Dictionary<String, ICommandParameter> parameters) { // , String groupName, String username) { ICommandResult result = null; String groupName = parameters["groupName"].First<String>(); String username = parameters["username"].First<String>(); if (this.DispatchPermissionsCheck(command, command.Name).Success == true) { GroupModel group = this.Groups.FirstOrDefault(g => g.Name == groupName); if (group != null) { if (group.IsGuest == false) { if (username.Length > 0) { AccountModel account = this.Groups.SelectMany(g => g.Accounts).FirstOrDefault(a => String.Compare(a.Username, username, StringComparison.OrdinalIgnoreCase) == 0); // If the account does not exist in any other group yet.. if (account == null) { account = new AccountModel() { Username = username, Group = group, }; group.Accounts.Add(account); result = new CommandResult() { Success = true, CommandResultType = CommandResultType.Success, Message = String.Format(@"Account ""{0}"" added to group ""{1}"".", account.Username, group.Name), Scope = new CommandData() { Groups = new List<GroupModel>() { group } }, Now = new CommandData() { Accounts = new List<AccountModel>() { account } } }; if (this.Shared.Events != null) { this.Shared.Events.Log(GenericEvent.ConvertToGenericEvent(result, GenericEventType.SecurityAccountAdded)); } } // Else the account exists already, relocate it. else { GroupModel existingGroup = account.Group; // Remove it from the other group account.Group.Accounts.Remove(account); // Add the account to this group. account.Group = group; group.Accounts.Add(account); result = new CommandResult() { Success = true, CommandResultType = CommandResultType.Success, Message = String.Format(@"Account ""{0}"" added to group ""{1}"".", account.Username, group.Name), Scope = new CommandData() { Accounts = new List<AccountModel>() { account } }, Then = new CommandData() { Groups = new List<GroupModel>() { existingGroup } }, Now = new CommandData() { Groups = new List<GroupModel>() { group } } }; if (this.Shared.Events != null) { this.Shared.Events.Log(GenericEvent.ConvertToGenericEvent(result, GenericEventType.SecurityAccountAdded)); } } } else { result = new CommandResult() { Success = false, CommandResultType = CommandResultType.InvalidParameter, Message = "An account username must not be zero length" }; } } else { result = new CommandResult() { Success = false, CommandResultType = CommandResultType.InvalidParameter, Message = "Cannot add an account to a guest group" }; } } else { result = new CommandResult() { Message = String.Format(@"Group with name ""{0}"" does not exists.", groupName), Success = false, CommandResultType = CommandResultType.DoesNotExists }; } } else { result = CommandResult.InsufficientPermissions; } return result; }
/// <summary> /// Helper for creating an access token. /// </summary> /// <param name="account">The account to create the access token for</param> /// <param name="identifier">The identifying peice of information to mixin with the token</param> /// <returns>An access token for transport, or null if the user can't have tokens or something went wrong while making the token.</returns> protected AccessTokenTransportModel GenerateAccessToken(AccountModel account, String identifier) { AccessTokenTransportModel accessTokenTransport = null; var accessToken = new AccessTokenModel() { Account = account, ExpiredWindowSeconds = this.Shared.Variables.Get(CommonVariableNames.SecurityMaximumAccessTokenLastTouchedLengthSeconds, 172800) }; var token = accessToken.Generate(identifier); if (String.IsNullOrEmpty(token) == false) { // Save the token hash for future authentication. this.Tunnel(CommandBuilder.SecurityAccountAppendAccessToken(account.Username, accessToken.Id, accessToken.TokenHash, accessToken.LastTouched).SetOrigin(CommandOrigin.Local)); accessTokenTransport = new AccessTokenTransportModel() { Id = accessToken.Id, Token = token }; } return accessTokenTransport; }
public void Dispose() { this.ProtocolType = CommonProtocolType.None; this.Uid = null; this.Account = null; }
/// <summary> /// Fetches a player in the current game connection that is /// asociated with the account executing this command. /// /// This is used so an account over a layer can issue a command like /// "kick me", but we only know "me" from the context of the account /// issuing the command. We use this to fetch the player in the game /// so we know who to kick. /// </summary> /// <param name="command"></param> /// <param name="speaker"></param> /// <returns></returns> protected PlayerModel GetAccountNetworkPlayer(ICommand command, AccountModel speaker) { PlayerModel player = this.Connection.ProtocolState.Players.Values.FirstOrDefault(p => p.Uid == command.Authentication.Uid); if (speaker != null) { AccountPlayerModel accountPlayer = speaker.Players.FirstOrDefault(p => p.ProtocolType == this.Connection.ConnectionModel.ProtocolType.Type); if (accountPlayer != null) { player = this.Connection.ProtocolState.Players.Values.FirstOrDefault(p => p.Uid == accountPlayer.Uid); } } return player; }
/// <summary> /// Runs through the various parsers for all of the text commands. /// </summary> /// <remarks> /// This method may fire multiple events to execute multiple commands /// when more than one parser is included in the future. This is expected /// behaviour. /// </remarks> /// <param name="speakerNetworkPlayer"></param> /// <param name="speakerAccount"></param> /// <param name="prefix"></param> /// <param name="text"></param> /// <returns>The generated event, if any.</returns> protected ICommandResult Parse(PlayerModel speakerNetworkPlayer, AccountModel speakerAccount, String prefix, String text) { List<ITextCommandParser> parsers = new List<ITextCommandParser>() { this.BuildFuzzyParser(speakerNetworkPlayer, speakerAccount), this.BuildRouteParser(speakerNetworkPlayer, speakerAccount) }; return parsers.Select(parser => parser.Parse(prefix, text)).FirstOrDefault(result => result != null); }
/// <summary> /// Fetches a route text parser /// </summary> /// <param name="speaker">The player executing the command</param> /// <param name="speakerAccount">The account executing the command</param> /// <returns>The route parser, provided a language could be found.</returns> protected ITextCommandParser BuildRouteParser(PlayerModel speaker, AccountModel speakerAccount) { return new RouteParser() { Connection = this.Connection, TextCommands = this.TextCommands.Where(textCommand => textCommand.Parser == TextCommandParserType.Any || textCommand.Parser == TextCommandParserType.Route).ToList(), SpeakerPlayer = speaker, SpeakerAccount = speakerAccount }; }
/// <summary> /// Fetches a fuzzy text parser /// </summary> /// <param name="speaker">The player executing the command</param> /// <param name="speakerAccount">The account executing the command</param> /// <returns>The fuzzy parser, provided a language could be found.</returns> protected ITextCommandParser BuildFuzzyParser(PlayerModel speaker, AccountModel speakerAccount) { LanguageConfig selectedLanguage = null; if (speakerAccount != null && speakerAccount.PreferredLanguageCode != String.Empty) { selectedLanguage = this.Shared.Languages.FindOptimalLanguageConfig(speakerAccount.PreferredLanguageCode); } else { selectedLanguage = this.Shared.Languages.Default; } return new FuzzyParser() { Connection = this.Connection, TextCommands = this.TextCommands.Where(textCommand => textCommand.Parser == TextCommandParserType.Any || textCommand.Parser == TextCommandParserType.Fuzzy).ToList(), Document = selectedLanguage.Config.Document, SpeakerPlayer = speaker, SpeakerAccount = speakerAccount }; }