/// <summary> /// Copies the permissions from one group to this group. /// </summary> /// <param name="command"></param> /// <param name="parameters"></param> /// <returns></returns> public ICommandResult SecurityGroupCopyPermissions(ICommand command, Dictionary<String, ICommandParameter> parameters) { ICommandResult result = null; String sourceGroupName = parameters["sourceGroupName"].First<String>(); String destinationGroupName = parameters["destinationGroupName"].First<String>(); if (this.DispatchPermissionsCheck(command, command.Name).Success == true) { GroupModel destinationGroup = this.Groups.FirstOrDefault(g => g.Name == destinationGroupName); if (destinationGroup != null) { GroupModel sourceGroup = this.Groups.FirstOrDefault(group => @group.Name == sourceGroupName); if (sourceGroup != null) { foreach (PermissionModel sourcePermission in sourceGroup.Permissions) { PermissionModel destinationPermission = destinationGroup.Permissions.FirstOrDefault(permission => sourcePermission.Name == permission.Name); if (destinationPermission != null) { destinationPermission.Authority = sourcePermission.Authority; } } result = new CommandResult() { Success = true, CommandResultType = CommandResultType.Success, Message = String.Format(@"Successfully copied permissions from group ""{0}"" to {1}.", sourceGroup.Name, destinationGroup.Name), Scope = new CommandData() { Groups = new List<GroupModel>() { destinationGroup } }, Now = new CommandData() { Permissions = destinationGroup.Permissions } }; if (this.Shared.Events != null) { this.Shared.Events.Log(GenericEvent.ConvertToGenericEvent(result, GenericEventType.SecurityGroupPermissionsCopied)); } } else { result = new CommandResult() { Success = false, CommandResultType = CommandResultType.DoesNotExists, Message = String.Format(@"Source group ""{0}"" does not exist.", sourceGroupName) }; } } else { result = new CommandResult() { Success = false, CommandResultType = CommandResultType.DoesNotExists, Message = String.Format(@"Destination group ""{0}"" does not exist.", sourceGroupName) }; } } else { result = CommandResult.InsufficientPermissions; } return result; }
/// <summary> /// Add a connection to this instance. /// </summary> /// <param name="command"></param> /// <param name="parameters"></param> public ICommandResult PotatoAddConnection(ICommand command, Dictionary<String, ICommandParameter> parameters) { ICommandResult result = null; String protocolTypeProvider = parameters["gameTypeProvider"].First<String>() ?? ""; String protocolTypeType = parameters["gameTypeType"].First<String>() ?? ""; String hostName = parameters["hostName"].First<String>() ?? ""; UInt16 port = parameters["port"].First<UInt16>(); String password = parameters["password"].First<String>() ?? ""; String additional = parameters["additional"].First<String>() ?? ""; // As long as the current account is allowed to execute this command... if (this.Shared.Security.DispatchPermissionsCheck(command, command.Name).Success == true) { // As long as we have less than the maximum amount of connections... if (this.Connections.Count < this.Shared.Variables.Get(CommonVariableNames.MaximumProtocolConnections, 9000)) { // As long as the connection for that specific game, hostname, and port does not exist... if (this.Connections.FirstOrDefault(c => c.ConnectionModel.ProtocolType.Type == protocolTypeType && c.ConnectionModel.Hostname == hostName && c.ConnectionModel.Port == port) == null) { // As long as the game type is defined... var supportCheckResult = this.Protocols.Tunnel(CommandBuilder.ProtocolsCheckSupportedProtocol(protocolTypeProvider, protocolTypeType).SetOrigin(CommandOrigin.Local)); if (supportCheckResult.Success == true) { IProtocolAssemblyMetadata meta = supportCheckResult.Now.ProtocolAssemblyMetadatas.First(); ConnectionController connection = new ConnectionController() { Potato = this }; connection.SetupProtocol(supportCheckResult.Now.ProtocolAssemblyMetadatas.First(), supportCheckResult.Now.ProtocolTypes.First(), new ProtocolSetup() { Hostname = hostName, Port = port, Password = password, Arguments = ArgumentHelper.ToArguments(additional.Wordify()), ConfigDirectory = meta.Directory.GetDirectories(Defines.ProtocolsDirectoryName, SearchOption.AllDirectories).Select(directory => directory.FullName).FirstOrDefault() }); lock (this.Connections) { this.Connections.Add(connection); } connection.Execute(); connection.AttemptConnection(); result = new CommandResult() { Message = String.Format("Successfully added {0} connection.", protocolTypeType), CommandResultType = CommandResultType.Success, Success = true, Now = { Connections = new List<ConnectionModel>() { connection.ConnectionModel } } }; this.Shared.Events.Log(GenericEvent.ConvertToGenericEvent(result, GenericEventType.PotatoConnectionAdded)); } else { result = new CommandResult() { Message = String.Format(@"Protocol type ""{0}"" is not supported.", protocolTypeType), CommandResultType = CommandResultType.DoesNotExists, Success = false }; } } else { result = new CommandResult() { Message = String.Format(@"Game type ""{0}"" with connection to {1}:{2} has already been added.", protocolTypeType, hostName, port), CommandResultType = CommandResultType.AlreadyExists, Success = false }; } } else { result = new CommandResult() { Message = String.Format(@"Maximum number of game connections exceeded."), CommandResultType = CommandResultType.LimitExceeded, Success = false }; } } else { result = CommandResult.InsufficientPermissions; } return result; }
/// <summary> /// Queries this instance for a snapshot as it exists now /// </summary> /// <param name="command"></param> /// <param name="parameters"></param> /// <returns></returns> public ICommandResult PotatoQuery(ICommand command, Dictionary<String, ICommandParameter> parameters) { ICommandResult result = null; if (this.Shared.Security.DispatchPermissionsCheck(command, command.Name).Success == true) { ICommandResult packages = this.Packages.Tunnel(new Command(command) { CommandType = CommandType.PackagesFetchPackages }); ICommandResult protocols = this.Protocols.Tunnel(new Command(command) { CommandType = CommandType.ProtocolsFetchSupportedProtocols }); result = new CommandResult() { Success = true, CommandResultType = CommandResultType.Success, Now = new CommandData() { Connections = this.Connections.Select(connection => connection.ConnectionModel).ToList(), ProtocolTypes = new List<ProtocolType>(protocols.Now.ProtocolTypes ?? new List<ProtocolType>()), Repositories = new List<RepositoryModel>(packages.Now.Repositories ?? new List<RepositoryModel>()), Groups = new List<Core.Shared.Models.GroupModel>(this.Shared.Security.Groups), Languages = this.Shared.Languages.LoadedLanguageFiles.Select(language => language.LanguageModel).ToList(), Variables = new List<VariableModel>(this.Shared.Variables.VolatileVariables.Values) } }; } else { result = CommandResult.InsufficientPermissions; } return result; }
/// <summary> /// Runs a query on a specific driver by its name. /// </summary> /// <param name="databaseGroupName">The name of the database group to use</param> /// <param name="queries">The queries to execute on the matching driver</param> /// <returns>The result of the commands containing the results of each query.</returns> protected ICommandResult ExecuteQueriesOnGroupName(String databaseGroupName, List<IDatabaseObject> queries) { ICommandResult result = null; if (this.OpenDrivers.ContainsKey(databaseGroupName) == true) { result = this.ExecuteQueriesOnDriver(this.OpenDrivers[databaseGroupName], queries); } else { result = new CommandResult() { Message = String.Format(@"Database driver ""{0}"" is not supported.", databaseGroupName), CommandResultType = CommandResultType.DoesNotExists, Success = false }; } return result; }
/// <summary> /// Posts a restart signal for the service controller to poll. /// </summary> /// <param name="command"></param> /// <param name="parameters"></param> /// <returns></returns> public ICommandResult PotatoServiceRestart(ICommand command, Dictionary<String, ICommandParameter> parameters) { ICommandResult result = null; // As long as the current account is allowed to execute this command... if (this.Shared.Security.DispatchPermissionsCheck(command, command.Name).Success == true) { this.ServiceMessage = new ServiceMessage() { Name = "restart" }; result = new CommandResult() { Message = String.Format("Successfully posted restart signal."), CommandResultType = CommandResultType.Success, Success = true }; this.Shared.Events.Log(GenericEvent.ConvertToGenericEvent(result, GenericEventType.PotatoServiceRestarting)); } else { result = CommandResult.InsufficientPermissions; } return result; }
/// <summary> /// Checks the authentication of the command against an account, seeing if they are identical /// (the command executor is the same as the account) /// </summary> /// <param name="command">The command to extract the executor from</param> /// <param name="gameType"></param> /// <param name="uid"></param> /// <returns>The result of the comparison</returns> public ICommandResult DispatchIdentityCheck(ICommand command, String gameType, String uid) { ICommandResult result = null; AccountModel executor = this.GetAccount(command); AccountModel target = this.GetAccount(gameType, uid); if (executor != null && executor.Equals(target) == true) { result = new CommandResult() { Success = true, CommandResultType = CommandResultType.Success }; } else { result = new CommandResult() { Success = false, CommandResultType = CommandResultType.Failed }; } return result; }
/// <summary> /// Sets a permission on the current group, provided the groupName parameter matches this group. /// </summary> /// <param name="command"></param> /// <param name="parameters"></param> /// <returns></returns> public ICommandResult SecurityGroupSetPermission(ICommand command, Dictionary<String, ICommandParameter> parameters) { ICommandResult result = null; String groupName = parameters["groupName"].First<String>(); String permissionName = parameters["permissionName"].First<String>(); int authority = parameters["authority"].First<int>(); if (this.DispatchPermissionsCheck(command, command.Name).Success == true) { // If it's the users group AND (the permission to set permissions OR the permission is to authenticate) AND they are changing the permission to nothing bool willResultInSystemLockout = this.DispatchGroupCheck(command, groupName).Success == true && (permissionName == CommandType.SecurityGroupSetPermission.ToString() || permissionName == CommandType.SecurityAccountAuthenticate.ToString()) && authority <= 0; if (willResultInSystemLockout == false) { GroupModel group = this.Groups.FirstOrDefault(g => g.Name == groupName); if (group != null) { // Fetch or create the permission. Should always exist in our config, even if it is null. // This also allows for new permissions to be added to CommandName in the future // without breaking old configs. PermissionModel permission = group.Permissions.FirstOrDefault(perm => perm.Name == permissionName); if (permission == null) { permission = new PermissionModel() { Name = permissionName, Authority = authority }; group.Permissions.Add(permission); } else { permission.Authority = authority; } result = new CommandResult() { Success = true, CommandResultType = CommandResultType.Success, Message = String.Format(@"Permission ""{0}"" set to {1}.", permission.Name, permission.Authority), Scope = new CommandData() { Groups = new List<GroupModel>() { group } }, Now = new CommandData() { Permissions = new List<PermissionModel>() { permission } } }; if (this.Shared.Events != null) { this.Shared.Events.Log(GenericEvent.ConvertToGenericEvent(result, GenericEventType.SecurityGroupPermissionAuthorityChanged)); } } else { result = new CommandResult() { Message = String.Format(@"Group with name ""{0}"" does not exists.", groupName), Success = false, CommandResultType = CommandResultType.DoesNotExists }; } } else { result = new CommandResult() { Message = String.Format(@"You cannot lock your group out of the system."), Success = false, CommandResultType = CommandResultType.InvalidParameter }; } } else { result = CommandResult.InsufficientPermissions; } return result; }
/// <summary> /// Authenticates an account /// </summary> /// <param name="command"></param> /// <param name="parameters"></param> /// <returns></returns> public ICommandResult SecurityAccountAuthenticate(ICommand command, Dictionary<String, ICommandParameter> parameters) { ICommandResult result = null; String username = parameters["username"].First<String>(); String passwordPlainText = parameters["passwordPlainText"].First<String>(); String identifier = parameters["identifier"].First<String>(); if (this.DispatchPermissionsCheck(command, command.Name).Success == true) { AccountModel account = this.Groups.SelectMany(g => g.Accounts).FirstOrDefault(a => String.Compare(a.Username, username, StringComparison.OrdinalIgnoreCase) == 0); if (account != null) { if (account.PasswordHash.Length > 0) { if (String.CompareOrdinal(account.PasswordHash, BCrypt.Net.BCrypt.HashPassword(passwordPlainText, account.PasswordHash)) == 0) { var accessTokenTransport = this.GenerateAccessToken(account, identifier); result = new CommandResult() { Success = true, CommandResultType = CommandResultType.Success, Message = String.Format(@"Successfully authenticated against account with username ""{0}"".", account.Username), Scope = { Accounts = new List<AccountModel>() { account }, AccessTokens = accessTokenTransport != null ? new List<AccessTokenTransportModel>() { accessTokenTransport } : null, Groups = new List<GroupModel>() { account.Group } } }; } else { result = new CommandResult() { Success = false, CommandResultType = CommandResultType.Failed, Message = "Invalid username or password." }; } } else { result = new CommandResult() { Success = false, CommandResultType = CommandResultType.Failed, Message = "Invalid username or password." }; } } else { result = new CommandResult() { Success = false, CommandResultType = CommandResultType.Failed, Message = "Invalid username or password." }; } } else { result = CommandResult.InsufficientPermissions; } return result; }
/// <summary> /// Authenticates an account against a access token /// </summary> public ICommandResult SecurityAccountAuthenticateToken(ICommand command, Dictionary<String, ICommandParameter> parameters) { ICommandResult result = null; Guid id = parameters["id"].First<Guid>(); String token = parameters["token"].First<String>(); String identifier = parameters["identifier"].First<String>(); if (this.DispatchPermissionsCheck(command, command.Name).Success == true) { AccessTokenModel accountAccessToken = this.Groups.SelectMany(group => group.Accounts).Where(account => account.AccessTokens.ContainsKey(id)).SelectMany(account => account.AccessTokens).Where(accessToken => accessToken.Key == id).Select(accessToken => accessToken.Value).FirstOrDefault(); if (accountAccessToken != null) { if (accountAccessToken.Authenticate(id, token, identifier)) { result = new CommandResult() { Success = true, CommandResultType = CommandResultType.Success, Message = String.Format(@"Successfully authenticated against account with username ""{0}"".", accountAccessToken.Account.Username), Scope = { Accounts = new List<AccountModel>() { accountAccessToken.Account }, Groups = new List<GroupModel>() { accountAccessToken.Account.Group } } }; if (this.Shared.Events != null) { this.Shared.Events.Log(GenericEvent.ConvertToGenericEvent(result, GenericEventType.SecurityAccountTokenAuthenticated)); } } else { result = new CommandResult() { Success = false, CommandResultType = CommandResultType.Failed, Message = "Invalid id or token." }; } } else { result = new CommandResult() { Success = false, CommandResultType = CommandResultType.Failed, Message = "Invalid id or token." }; } } else { result = CommandResult.InsufficientPermissions; } return result; }
/// <summary> /// Sets the password hash without any other processing. Used when loading from a config. /// </summary> /// <param name="command"></param> /// <param name="parameters"></param> /// <returns></returns> public ICommandResult SecurityAccountSetPasswordHash(ICommand command, Dictionary<String, ICommandParameter> parameters) { ICommandResult result = null; String username = parameters["username"].First<String>(); String passwordHash = parameters["passwordHash"].First<String>(); if (this.DispatchPermissionsCheck(command, command.Name).Success == true) { AccountModel account = this.Groups.SelectMany(g => g.Accounts).FirstOrDefault(a => String.Compare(a.Username, username, StringComparison.OrdinalIgnoreCase) == 0); if (account != null) { if (passwordHash.Length > 0) { account.PasswordHash = passwordHash; result = new CommandResult() { Success = true, CommandResultType = CommandResultType.Success, Message = String.Format(@"Successfully set password for account with username ""{0}"".", account.Username) }; } else { result = new CommandResult() { Success = false, CommandResultType = CommandResultType.InvalidParameter, Message = "A password hash must not be zero length" }; } } else { result = new CommandResult() { Message = String.Format(@"Account with username ""{0}"" does not exists.", username), Success = false, CommandResultType = CommandResultType.DoesNotExists }; } } else { result = CommandResult.InsufficientPermissions; } return result; }
/// <summary> /// Appends an access token onto an accounts acceptable token list. /// </summary> public ICommandResult SecurityAccountAppendAccessToken(ICommand command, Dictionary<String, ICommandParameter> parameters) { ICommandResult result = null; String username = parameters["username"].First<String>(); Guid id = parameters["id"].First<Guid>(); String tokenHash = parameters["tokenHash"].First<String>(); DateTime lastTouched = parameters["lastTouched"].First<DateTime>(); if (this.DispatchPermissionsCheck(command, command.Name).Success == true) { AccountModel account = this.Groups.SelectMany(g => g.Accounts).FirstOrDefault(a => String.Compare(a.Username, username, StringComparison.OrdinalIgnoreCase) == 0); if (account != null) { if (id != Guid.Empty && tokenHash.Length > 0 && lastTouched > DateTime.Now.AddSeconds(-1 * Math.Abs(this.Shared.Variables.Get(CommonVariableNames.SecurityMaximumAccessTokenLastTouchedLengthSeconds, 172800)))) { // Upsert the token hash account.AccessTokens.AddOrUpdate(id, guid => new AccessTokenModel() { Id = id, Account = account, TokenHash = tokenHash, LastTouched = lastTouched, ExpiredWindowSeconds = this.Shared.Variables.Get(CommonVariableNames.SecurityMaximumAccessTokenLastTouchedLengthSeconds, 172800) }, (guid, model) => { model.TokenHash = tokenHash; model.LastTouched = lastTouched; return model; }); // Keep removing token hashes if we've added too many while (account.AccessTokens.Count > 0 && account.AccessTokens.Count > this.Shared.Variables.Get(CommonVariableNames.SecurityMaximumAccessTokensPerAccount, 5)) { var oldestId = account.AccessTokens.OrderBy(accessToken => accessToken.Value.LastTouched).First(); // Remove the token that was touched the longest ago. AccessTokenModel removed; account.AccessTokens.TryRemove(oldestId.Key, out removed); } result = new CommandResult() { Success = true, CommandResultType = CommandResultType.Success, Message = String.Format(@"Successfully added token hash to account ""{0}"".", account.Username) }; } else { result = new CommandResult() { Success = false, CommandResultType = CommandResultType.InvalidParameter, Message = "An id or tokenHash must not be empty and a lastTouched not expired" }; } } else { result = new CommandResult() { Message = String.Format(@"Account with username ""{0}"" does not exists.", username), Success = false, CommandResultType = CommandResultType.DoesNotExists }; } } else { result = CommandResult.InsufficientPermissions; } return result; }
/// <summary> /// Potato.private.account.setPassword "Phogue" "pass" /// Potato.private.account.setPassword "Hassan" "password1" /// </summary> /// <param name="command"></param> /// <param name="parameters"></param> public ICommandResult SecurityAccountSetPassword(ICommand command, Dictionary<String, ICommandParameter> parameters) { ICommandResult result = null; // <param name="username">The unique name of the account. Account.Name</param> // <param name="password">The person password to login to the layer. Account.Password</param> String username = parameters["username"].First<String>(); String password = parameters["password"].First<String>(); if (this.DispatchPermissionsCheck(command, command.Name).Success == true) { AccountModel account = this.Groups.SelectMany(g => g.Accounts).FirstOrDefault(a => String.Compare(a.Username, username, StringComparison.OrdinalIgnoreCase) == 0); if (account != null) { if (password.Length > 0) { account.PasswordHash = BCrypt.Net.BCrypt.HashPassword(password, BCrypt.Net.BCrypt.GenerateSalt()); result = new CommandResult() { Success = true, CommandResultType = CommandResultType.Success, Message = String.Format(@"Successfully changed password for account with username ""{0}"".", account.Username) }; } else { result = new CommandResult() { Success = false, CommandResultType = CommandResultType.InvalidParameter, Message = "A password must not be zero length" }; } } else { result = new CommandResult() { Message = String.Format(@"Account with username ""{0}"" does not exists.", username), Success = false, CommandResultType = CommandResultType.DoesNotExists }; } } else { result = CommandResult.InsufficientPermissions; } return result; }
/// <summary> /// Potato.private.account.assign "Phogue" "CallOfDuty" "101478382" -- guid /// Potato.private.account.assign "Phogue" "BFBC2" "ABCDABCDABCD" -- cdkey /// </summary> /// <param name="command"></param> /// <param name="parameters"></param> public ICommandResult SecurityAccountAddPlayer(ICommand command, Dictionary<String, ICommandParameter> parameters) { ICommandResult result = null; // <param name="username">The unique name of the account. Account.Name</param> // <param name="gameType">The name of the game, found in Potato.Core.Connections.Support</param> // <param name="uid">The UID of the player by cd key, name - etc.</param> String username = parameters["username"].First<String>(); String gameType = parameters["gameType"].First<String>(); String uid = parameters["uid"].First<String>(); if (this.DispatchPermissionsCheck(command, command.Name).Success == true) { AccountModel account = this.Groups.SelectMany(g => g.Accounts).FirstOrDefault(a => String.Compare(a.Username, username, StringComparison.OrdinalIgnoreCase) == 0); if (account != null) { if (uid.Length > 0) { AccountPlayerModel player = this.Groups.SelectMany(group => @group.Accounts) .SelectMany(a => a.Players) .FirstOrDefault(x => x.ProtocolType == gameType && x.Uid == uid); // If the player does not exist for any other player.. if (player == null) { player = new AccountPlayerModel() { ProtocolType = gameType, Uid = uid, Account = account }; account.Players.Add(player); result = new CommandResult() { Success = true, CommandResultType = CommandResultType.Success, Message = String.Format(@"Player with UID of ""{0}"" in game type ""{1}"" added to account ""{2}"".", player.Uid, player.ProtocolType, account.Username), Scope = new CommandData() { Accounts = new List<AccountModel>() { account }, Groups = new List<GroupModel>() { account.Group } }, Now = new CommandData() { AccountPlayers = new List<AccountPlayerModel>() { player } } }; if (this.Shared.Events != null) { this.Shared.Events.Log(GenericEvent.ConvertToGenericEvent(result, GenericEventType.SecurityPlayerAdded)); } } // Else the player already exists and is attached to another account. Reassign it. else { AccountModel existingAccount = player.Account; // Remove the player from the other account player.Account.Players.Remove(player); // Add the player to this account. player.Account = account; account.Players.Add(player); result = new CommandResult() { Success = true, CommandResultType = CommandResultType.Success, Message = String.Format(@"Player with UID of ""{0}"" in game type ""{1}"" added to account ""{2}"".", player.Uid, player.ProtocolType, account.Username), Scope = new CommandData() { AccountPlayers = new List<AccountPlayerModel>() { player } }, Then = new CommandData() { Accounts = new List<AccountModel>() { existingAccount } }, Now = new CommandData() { Accounts = new List<AccountModel>() { account } } }; if (this.Shared.Events != null) { this.Shared.Events.Log(GenericEvent.ConvertToGenericEvent(result, GenericEventType.SecurityPlayerAdded)); } } } else { result = new CommandResult() { Success = false, CommandResultType = CommandResultType.InvalidParameter, Message = "A player uid must not be zero length" }; } } else { result = new CommandResult() { Message = String.Format(@"Account with username ""{0}"" does not exists.", username), Success = false, CommandResultType = CommandResultType.DoesNotExists }; } } else { result = CommandResult.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> /// 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> /// Potato.private.account.setPreferredLanguageCode "Phogue" "en" /// </summary> /// <param name="command"></param> /// <param name="parameters"></param> public ICommandResult SecurityAccountSetPreferredLanguageCode(ICommand command, Dictionary<String, ICommandParameter> parameters) { ICommandResult result = null; // <param name="username">The unique name of the account. Account.Name</param> // <param name="languageCode">ISO 639-1 preferred language code</param> String username = parameters["username"].First<String>(); String languageCode = parameters["languageCode"].First<String>(); // If the user has permission or they are setting their own authenticated account. if (this.DispatchPermissionsCheck(command, command.Name).Success == true || this.DispatchIdentityCheck(command, username).Success == true) { AccountModel account = this.Groups.SelectMany(g => g.Accounts).FirstOrDefault(a => String.Compare(a.Username, username, StringComparison.OrdinalIgnoreCase) == 0); if (account != null) { LanguageModel language = this.Shared.Languages.LoadedLanguageFiles.Where(l => l.LanguageModel.LanguageCode == languageCode).Select(l => l.LanguageModel).FirstOrDefault(); // If we have the language code we fixup the casing just to be pretty and stuff. if (language != null) { languageCode = language.LanguageCode; } account.PreferredLanguageCode = languageCode; result = new CommandResult() { Success = true, CommandResultType = CommandResultType.Success, Message = String.Format(@"Account with username ""{0}"" set preferred language to ""{1}"".", account.Username, languageCode) }; } else { result = new CommandResult() { Message = String.Format(@"Account with username ""{0}"" does not exists.", username), Success = false, CommandResultType = CommandResultType.DoesNotExists }; } } else { result = CommandResult.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> /// Sets a group's permissions to a predefined list of permissions required /// for a simple streaming account. /// </summary> public ICommandResult SecuritySetPredefinedStreamPermissions(ICommand command, Dictionary<String, ICommandParameter> parameters) { ICommandResult result = null; String groupName = parameters["groupName"].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) { // A list of permissions to keep as "1", all others will be nulled out. List<CommandType> permissions = new List<CommandType>() { CommandType.PotatoPing, CommandType.SecurityAccountAuthenticate, CommandType.EventsEstablishJsonStream, CommandType.PotatoQuery, CommandType.ProtocolsFetchSupportedProtocols, CommandType.PackagesFetchPackages, CommandType.ConnectionQuery, CommandType.NetworkProtocolQueryBans, CommandType.NetworkProtocolQueryMapPool, CommandType.NetworkProtocolQueryMaps, CommandType.NetworkProtocolQueryPlayers, CommandType.NetworkProtocolQuerySettings }; foreach (var permission in group.Permissions) { permission.Authority = permissions.Contains(permission.CommandType) ? 1 : (int?)null; } result = new CommandResult() { Success = true, CommandResultType = CommandResultType.Success, Message = String.Format(@"Group with name ""{0}"" set permissions to predefined stream setup.", group.Name) }; } 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> /// Checks the authentication of the command's group against a group name, checking if they /// are identical (the command executor belongs to a specific group) /// </summary> /// <param name="command">The command to extract the executor from</param> /// <param name="groupName">The name of the group to check against</param> /// <returns>The result of the comparison</returns> public ICommandResult DispatchGroupCheck(ICommand command, String groupName) { ICommandResult result = null; AccountModel executor = this.GetAccount(command); if (executor != null && executor.Group != null && executor.Group.Name == groupName) { result = new CommandResult() { Success = true, CommandResultType = CommandResultType.Success }; } else { result = new CommandResult() { Success = false, CommandResultType = CommandResultType.Failed }; } return result; }
/// <summary> /// Sets a group's permissions to maximo for the true Administrator experience. /// </summary> public ICommandResult SecuritySetPredefinedAdministratorsPermissions(ICommand command, Dictionary<String, ICommandParameter> parameters) { ICommandResult result = null; String groupName = parameters["groupName"].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) { foreach (var permission in group.Permissions) { permission.Authority = 2; } result = new CommandResult() { Success = true, CommandResultType = CommandResultType.Success, Message = String.Format(@"Group with name ""{0}"" set permissions to predefined administrator setup.", group.Name) }; } 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> /// Executes the list of queries, returning the results of the queries. /// </summary> /// <param name="driver">The driver to execute the query on</param> /// <param name="queries">The queries to execute</param> /// <returns>The result of the commands containing the results of each query.</returns> protected ICommandResult ExecuteQueriesOnDriver(IDriver driver, List<IDatabaseObject> queries) { ICommandResult result = null; result = new CommandResult() { Success = true, CommandResultType = CommandResultType.Success, Then = { Queries = new List<IDatabaseObject>(queries) }, Now = { Queries = new List<IDatabaseObject>() } }; foreach (IDatabaseObject query in queries) { // todo is this correct, or should it instead have a CollectionValue? result.Now.Queries.AddRange(driver.Query(query)); } return result; }
/// <summary> /// Creates a new group if the specified name is unique. /// </summary> public ICommandResult SecurityAddGroup(ICommand command, Dictionary<String, ICommandParameter> parameters) { ICommandResult result = null; String groupName = parameters["groupName"].First<String>(); if (this.DispatchPermissionsCheck(command, command.Name).Success == true) { if (groupName.Length > 0) { if (this.Groups.FirstOrDefault(group => @group.Name == groupName) == null) { GroupModel group = new GroupModel() { Name = groupName }; this.Groups.Add(group); result = new CommandResult() { Success = true, CommandResultType = CommandResultType.Success, Message = String.Format(@"Group ""{0}"" created.", groupName), Now = new CommandData() { Groups = new List<GroupModel>() { group } } }; if (this.Shared.Events != null) { this.Shared.Events.Log(GenericEvent.ConvertToGenericEvent(result, GenericEventType.SecurityGroupAdded)); } } else { result = new CommandResult() { Success = false, CommandResultType = CommandResultType.AlreadyExists, Message = String.Format(@"Group ""{0}"" already exists.", groupName) }; } } else { result = new CommandResult() { Success = false, CommandResultType = CommandResultType.InvalidParameter, Message = "A group name must not be zero length" }; } } else { result = CommandResult.InsufficientPermissions; } return result; }
/// <summary> /// This function is similar to that found in Potato.Core.Connections.Connection.NetworkProtocolActionKick /// </summary> /// <param name="command"></param> /// <param name="parameters"></param> /// <returns></returns> public ICommandResult NetworkProtocolActionKick(ICommand command, Dictionary<String, ICommandParameter> parameters) { ICommandResult result = new CommandResult() { CommandResultType = CommandResultType.Success, Success = true }; // You can ignore this. This is a mock of Potato's internal process, but looks nothing like it really INetworkAction kick = parameters["action"].First<INetworkAction>(); List<IPacket> requests = new List<IPacket>() { new Packet() { RequestId = 100, Words = new List<string>() { "admin.kickPlayer", kick.Scope.Players.First().Name }, DebugText = String.Format("[0-admin.kickPlayer] [1-{0}]", kick.Scope.Players.First().Name), Origin = PacketOrigin.Client, Type = PacketType.Request }, new Packet() { RequestId = 101, Words = new List<string>() { "admin.say", kick.Scope.Content.First(), "player", kick.Scope.Players.First().Name, }, DebugText = String.Format("[0-admin.say] [1-{0}] [2-player] [3-{1}]", kick.Scope.Content.First(), kick.Scope.Players.First().Name), Origin = PacketOrigin.Client, Type = PacketType.Request } }; this.Waiting.Wait(new NetworkAction() { ActionType = NetworkActionType.NetworkPlayerKick, Scope = kick.Scope, Now = kick.Now, Then = kick.Then }, requests); result.Now.Packets = requests; return result; }
/// <summary> /// Removes the group whose name is specified. /// </summary> /// <param name="command"></param> /// <param name="parameters"></param> /// <returns></returns> public ICommandResult SecurityRemoveGroup(ICommand command, Dictionary<String, ICommandParameter> parameters) { ICommandResult result = null; String groupName = parameters["groupName"].First<String>(); if (this.DispatchPermissionsCheck(command, command.Name).Success == true) { if (groupName.Length > 0) { if (this.DispatchGroupCheck(command, groupName).Success == false) { GroupModel group = this.Groups.FirstOrDefault(g => g.Name == groupName); if (group != null) { if (group.IsGuest == false) { Groups.Remove(group); result = new CommandResult() { Success = true, CommandResultType = CommandResultType.Success, Message = String.Format(@"Group ""{0}"" removed.", groupName), Then = new CommandData() { Groups = new List<GroupModel>() { group.Clone() as GroupModel } } }; if (this.Shared.Events != null) { this.Shared.Events.Log(GenericEvent.ConvertToGenericEvent(result, GenericEventType.SecurityGroupRemoved)); } // Now cleanup our stored account group.Dispose(); } else { result = new CommandResult() { Success = false, CommandResultType = CommandResultType.InvalidParameter, Message = "Cannot delete the guest group" }; } } else { result = new CommandResult() { Success = false, CommandResultType = CommandResultType.DoesNotExists, Message = String.Format(@"Group ""{0}"" does not exist.", groupName) }; } } else { result = new CommandResult() { Success = false, CommandResultType = CommandResultType.InvalidParameter, Message = "Cannot delete the your own group" }; } } else { result = new CommandResult() { Success = false, CommandResultType = CommandResultType.InvalidParameter, Message = "A group name must not be zero length" }; } } else { result = CommandResult.InsufficientPermissions; } return result; }
/// <summary> /// Posts a uninstall signal for the service controller to poll. /// </summary> /// <param name="command"></param> /// <param name="parameters"></param> /// <returns></returns> public ICommandResult PotatoServiceUninstallPackage(ICommand command, Dictionary<String, ICommandParameter> parameters) { ICommandResult result = null; String packageId = parameters["packageId"].First<String>(); // As long as the current account is allowed to execute this command... if (this.Shared.Security.DispatchPermissionsCheck(command, command.Name).Success == true) { if (String.IsNullOrEmpty(packageId) == false) { this.ServiceMessage = new ServiceMessage() { Name = "uninstall", Arguments = new Dictionary<String, String>() { { "packageid", packageId } } }; result = new CommandResult() { Message = String.Format("Successfully posted uninstall signal."), CommandResultType = CommandResultType.Success, Success = true }; this.Shared.Events.Log(GenericEvent.ConvertToGenericEvent(result, GenericEventType.PotatoServiceUninstallPackage)); } else { result = new CommandResult() { Message = String.Format(@"Invalid or missing parameter ""packageId""."), CommandResultType = CommandResultType.InvalidParameter, Success = false }; } } else { result = CommandResult.InsufficientPermissions; } return result; }
/// <summary> /// Removes an account, whatever group it is assigned to. /// </summary> public ICommandResult SecurityRemoveAccount(ICommand command, Dictionary<String, ICommandParameter> parameters) { ICommandResult result = null; String username = parameters["username"].First<String>(); if (this.DispatchPermissionsCheck(command, command.Name).Success == true) { if (username.Length > 0) { if (this.DispatchIdentityCheck(command, username).Success == false) { // Fetch the account, whatever group it is added to. AccountModel account = this.Groups.SelectMany(group => @group.Accounts).FirstOrDefault(a => String.Compare(a.Username, username, StringComparison.OrdinalIgnoreCase) == 0); if (account != null) { account.Group.Accounts.Remove(account); result = new CommandResult() { Success = true, CommandResultType = CommandResultType.Success, Message = String.Format(@"Account ""{0}"" removed.", account.Username), Then = new CommandData() { Accounts = new List<AccountModel>() { account.Clone() as AccountModel }, Groups = new List<GroupModel>() { account.Group } } }; if (this.Shared.Events != null) { this.Shared.Events.Log(GenericEvent.ConvertToGenericEvent(result, GenericEventType.SecurityAccountRemoved)); } // Now cleanup our stored account account.Dispose(); } else { result = new CommandResult() { Success = false, CommandResultType = CommandResultType.DoesNotExists, Message = String.Format(@"Account ""{0}"" does not exist.", username) }; } } else { result = new CommandResult() { Success = false, CommandResultType = CommandResultType.InvalidParameter, Message = "Cannot remove your own account" }; } } else { result = new CommandResult() { Success = false, CommandResultType = CommandResultType.InvalidParameter, Message = "An account name must not be zero length" }; } } else { result = CommandResult.InsufficientPermissions; } return result; }
protected ICommandResult PotatoRemoveConnection(ICommand command, IConnectionController connection) { ICommandResult result = null; // As long as the current account is allowed to execute this command... if (this.Shared.Security.DispatchPermissionsCheck(command, command.Name).Success == true) { // As long as the connection for that specific game, hostname, and port exists... if (connection != null) { lock (this.Connections) { this.Connections.Remove(connection); } result = new CommandResult() { Message = String.Format(@"Successfully removed connection with connection to {0}:{1} and game type ""{2}"".", connection.ConnectionModel.Hostname, connection.ConnectionModel.Port, connection), CommandResultType = CommandResultType.Success, Success = true, Now = { Connections = new List<ConnectionModel>() { connection.ConnectionModel } } }; this.Shared.Events.Log(GenericEvent.ConvertToGenericEvent(result, GenericEventType.PotatoConnectionRemoved)); connection.Dispose(); } else { result = new CommandResult() { Message = String.Format(@"Connection does not exist."), CommandResultType = CommandResultType.DoesNotExists, Success = false }; } } else { result = CommandResult.InsufficientPermissions; } return result; }
/// <summary> /// Potato.private.account.revoke "Phogue" "CallOfDuty" "101478382" -- guid /// Potato.private.account.revoke "Phogue" "BFBC2" "ABCDABCDABCD" -- cdkey /// </summary> /// <param name="command"></param> /// <param name="parameters"></param> public ICommandResult SecurityRemovePlayer(ICommand command, Dictionary<String, ICommandParameter> parameters) { // (Command command, String gameType, String uid) { ICommandResult result = null; String gameType = parameters["gameType"].First<String>(); String uid = parameters["uid"].First<String>(); if (this.DispatchPermissionsCheck(command, command.Name).Success == true) { if (uid.Length > 0) { AccountPlayerModel player = this.Groups.SelectMany(group => @group.Accounts) .SelectMany(account => account.Players).FirstOrDefault(x => x.ProtocolType == gameType && x.Uid == uid); // If the player exists for any other player.. if (player != null) { // Remove the player from its account. player.Account.Players.Remove(player); result = new CommandResult() { Success = true, CommandResultType = CommandResultType.Success, Message = String.Format(@"Player with UID of ""{0}"" in game type ""{1}"" removed from account ""{2}"".", player.Uid, player.ProtocolType, player.Account.Username), Then = new CommandData() { AccountPlayers = new List<AccountPlayerModel>() { player.Clone() as AccountPlayerModel }, Accounts = new List<AccountModel>() { player.Account } } }; if (this.Shared.Events != null) { this.Shared.Events.Log(GenericEvent.ConvertToGenericEvent(result, GenericEventType.SecurityPlayerRemoved)); } // Now cleanup our stored player player.Dispose(); } else { result = new CommandResult() { Success = false, CommandResultType = CommandResultType.DoesNotExists, Message = String.Format(@"Player with UID of ""{0}"" in game type ""{1}"" does not exist.", uid, gameType) }; } } else { result = new CommandResult() { Success = false, CommandResultType = CommandResultType.InvalidParameter, Message = "A player uid must not be zero length" }; } } else { result = CommandResult.InsufficientPermissions; } return result; }
/// <summary> /// Queries this instance for a response and uptime /// </summary> /// <returns></returns> public ICommandResult PotatoPing(ICommand command, Dictionary<String, ICommandParameter> parameters) { ICommandResult result = null; if (this.Shared.Security.DispatchPermissionsCheck(command, command.Name).Success == true) { result = new CommandResult() { Success = true, CommandResultType = CommandResultType.Success, Now = { Content = new List<String>() { Convert.ToInt32((DateTime.Now - this.InstantiatedStamp).TotalMilliseconds).ToString(CultureInfo.InvariantCulture) } } }; } else { result = CommandResult.InsufficientPermissions; } return result; }
/// <summary> /// Sets the description of a permission /// </summary> /// <param name="command"></param> /// <param name="parameters"></param> /// <returns></returns> public ICommandResult SecurityGroupSetPermissionDescription(ICommand command, Dictionary<String, ICommandParameter> parameters) { ICommandResult result = null; String groupName = parameters["groupName"].First<String>(); String permissionName = parameters["permissionName"].First<String>(); String description = parameters["description"].First<String>(); if (this.DispatchPermissionsCheck(command, command.Name).Success == true) { GroupModel group = this.Groups.FirstOrDefault(g => g.Name == groupName); if (group != null) { // Fetch or create the permission. Should always exist in our config, even if it is null. // This also allows for new permissions to be added to CommandName in the future // without breaking old configs. PermissionModel permission = group.Permissions.FirstOrDefault(perm => perm.Name == permissionName); if (permission == null) { permission = new PermissionModel() { Name = permissionName, Description = description }; group.Permissions.Add(permission); } else { permission.Description = description; } result = new CommandResult() { Success = true, CommandResultType = CommandResultType.Success, Message = String.Format(@"Permission ""{0}"" set the description {1}.", permission.Name, description), Scope = new CommandData() { Groups = new List<GroupModel>() { group } }, Now = new CommandData() { Permissions = new List<PermissionModel>() { permission } } }; if (this.Shared.Events != null) { this.Shared.Events.Log(GenericEvent.ConvertToGenericEvent(result, GenericEventType.SecurityGroupPermissionTraitAppended)); } } 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; }