mirror of
https://github.com/AuthMe/AuthMeReloaded.git
synced 2024-12-24 17:47:38 +01:00
Merge pull request #1386 from AuthMe/1367-mysql-notNull-toggle
Improve MySQL not null toggle / authme debug commands output
This commit is contained in:
commit
8127d70058
@ -42,6 +42,7 @@ class CountryLookup implements DebugSection {
|
||||
|
||||
@Override
|
||||
public void execute(CommandSender sender, List<String> arguments) {
|
||||
sender.sendMessage(ChatColor.BLUE + "AuthMe country lookup");
|
||||
if (arguments.isEmpty()) {
|
||||
sender.sendMessage("Check player: /authme debug cty Bobby");
|
||||
sender.sendMessage("Check IP address: /authme debug cty 127.123.45.67");
|
||||
|
@ -10,6 +10,7 @@ import fr.xephi.authme.initialization.SettingsDependent;
|
||||
import fr.xephi.authme.initialization.factory.SingletonStore;
|
||||
import fr.xephi.authme.permission.DebugSectionPermissions;
|
||||
import fr.xephi.authme.permission.PermissionNode;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import javax.inject.Inject;
|
||||
@ -47,6 +48,7 @@ class DataStatistics implements DebugSection {
|
||||
|
||||
@Override
|
||||
public void execute(CommandSender sender, List<String> arguments) {
|
||||
sender.sendMessage(ChatColor.BLUE + "AuthMe statistics");
|
||||
sender.sendMessage("LimboPlayers in memory: " + applyToLimboPlayersMap(limboService, Map::size));
|
||||
sender.sendMessage("PlayerCache size: " + playerCache.getLogged() + " (= logged in players)");
|
||||
|
||||
@ -68,11 +70,10 @@ class DataStatistics implements DebugSection {
|
||||
}
|
||||
|
||||
private void outputInjectorStats(CommandSender sender) {
|
||||
sender.sendMessage(
|
||||
String.format("Singleton Java classes: %d (Reloadable: %d / SettingsDependent: %d / HasCleanup: %d)",
|
||||
singletonStore.retrieveAllOfType().size(),
|
||||
singletonStore.retrieveAllOfType(Reloadable.class).size(),
|
||||
singletonStore.retrieveAllOfType(SettingsDependent.class).size(),
|
||||
singletonStore.retrieveAllOfType(HasCleanup.class).size()));
|
||||
sender.sendMessage("Singleton Java classes: " + singletonStore.retrieveAllOfType().size());
|
||||
sender.sendMessage(String.format("(Reloadable: %d / SettingsDependent: %d / HasCleanup: %d)",
|
||||
singletonStore.retrieveAllOfType(Reloadable.class).size(),
|
||||
singletonStore.retrieveAllOfType(SettingsDependent.class).size(),
|
||||
singletonStore.retrieveAllOfType(HasCleanup.class).size()));
|
||||
}
|
||||
}
|
||||
|
@ -49,6 +49,7 @@ public class DebugCommand implements ExecutableCommand {
|
||||
}
|
||||
|
||||
private void sendAvailableSections(CommandSender sender) {
|
||||
sender.sendMessage(ChatColor.BLUE + "AuthMe debug utils");
|
||||
sender.sendMessage("Sections available to you:");
|
||||
long availableSections = getSections().values().stream()
|
||||
.filter(section -> permissionsManager.hasPermission(sender, section.getRequiredPermission()))
|
||||
|
@ -42,11 +42,12 @@ class HasPermissionChecker implements DebugSection {
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Checks if player has given permission: /authme debug perm bobby my.perm";
|
||||
return "Checks if a player has a given permission";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(CommandSender sender, List<String> arguments) {
|
||||
sender.sendMessage(ChatColor.BLUE + "AuthMe permission check");
|
||||
if (arguments.size() < 2) {
|
||||
sender.sendMessage("Check if a player has permission:");
|
||||
sender.sendMessage("Example: /authme debug perm bobby my.perm.node");
|
||||
|
@ -40,7 +40,7 @@ class InputValidator implements DebugSection {
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Check if email / password is valid according to your settings";
|
||||
return "Checks if your config.yml allows a password / email";
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -68,16 +68,18 @@ class InputValidator implements DebugSection {
|
||||
}
|
||||
|
||||
private void displayUsageHint(CommandSender sender) {
|
||||
sender.sendMessage("You can define forbidden emails and passwords in your config.yml");
|
||||
sender.sendMessage("This command allows you to test some of the values:");
|
||||
sender.sendMessage("/authme debug valid pass test1234 -- test if 'test1234' is allowed password");
|
||||
sender.sendMessage("/authme debug valid mail t@t.tld -- test if 't@t.tld' is allowed email");
|
||||
sender.sendMessage("/authme debug valid name bobby1 -- test if 'bobby1' is allowed username");
|
||||
sender.sendMessage(ChatColor.BLUE + "Validation tests");
|
||||
sender.sendMessage("You can define forbidden emails and passwords in your config.yml."
|
||||
+ " You can test your settings with this command.");
|
||||
final String command = ChatColor.GOLD + "/authme debug valid";
|
||||
sender.sendMessage(" Use " + command + " pass <pass>" + ChatColor.RESET + " to check a password");
|
||||
sender.sendMessage(" Use " + command + " mail <mail>" + ChatColor.RESET + " to check an email");
|
||||
sender.sendMessage(" Use " + command + " name <name>" + ChatColor.RESET + " to check a username");
|
||||
}
|
||||
|
||||
private void validatePassword(CommandSender sender, String password) {
|
||||
ValidationResult validationResult = validationService.validatePassword(password, "");
|
||||
sender.sendMessage("Validation of password '" + password + "' returned:");
|
||||
sender.sendMessage(ChatColor.BLUE + "Validation of password '" + password + "'");
|
||||
if (validationResult.hasError()) {
|
||||
messages.send(sender, validationResult.getMessageKey(), validationResult.getArgs());
|
||||
} else {
|
||||
@ -87,7 +89,7 @@ class InputValidator implements DebugSection {
|
||||
|
||||
private void validateEmail(CommandSender sender, String email) {
|
||||
boolean isValidEmail = validationService.validateEmail(email);
|
||||
sender.sendMessage("Validation of email '" + email + "' returned:");
|
||||
sender.sendMessage(ChatColor.BLUE + "Validation of email '" + email + "'");
|
||||
if (isValidEmail) {
|
||||
sender.sendMessage(ChatColor.DARK_GREEN + "Valid email!");
|
||||
} else {
|
||||
@ -96,7 +98,7 @@ class InputValidator implements DebugSection {
|
||||
}
|
||||
|
||||
private void validateUsername(CommandSender sender, String username) {
|
||||
sender.sendMessage("Validation of username '" + username + "' returned:");
|
||||
sender.sendMessage(ChatColor.BLUE + "Validation of username '" + username + "'");
|
||||
try {
|
||||
onJoinVerifier.checkIsValidName(username);
|
||||
sender.sendMessage("Valid username!");
|
||||
|
@ -50,6 +50,7 @@ class LimboPlayerViewer implements DebugSection {
|
||||
@Override
|
||||
public void execute(CommandSender sender, List<String> arguments) {
|
||||
if (arguments.isEmpty()) {
|
||||
sender.sendMessage(ChatColor.BLUE + "AuthMe limbo viewer");
|
||||
sender.sendMessage("/authme debug limbo <player>: show a player's limbo info");
|
||||
sender.sendMessage("Available limbo records: " + applyToLimboPlayersMap(limboService, Map::keySet));
|
||||
return;
|
||||
@ -59,11 +60,12 @@ class LimboPlayerViewer implements DebugSection {
|
||||
Player player = bukkitService.getPlayerExact(arguments.get(0));
|
||||
LimboPlayer diskLimbo = player != null ? limboPersistence.getLimboPlayer(player) : null;
|
||||
if (memoryLimbo == null && player == null) {
|
||||
sender.sendMessage("No limbo info and no player online with name '" + arguments.get(0) + "'");
|
||||
sender.sendMessage(ChatColor.BLUE + "No AuthMe limbo data");
|
||||
sender.sendMessage("No limbo data and no player online with name '" + arguments.get(0) + "'");
|
||||
return;
|
||||
}
|
||||
|
||||
sender.sendMessage(ChatColor.GOLD + "Showing player / limbo / disk limbo info for '" + arguments.get(0) + "'");
|
||||
sender.sendMessage(ChatColor.BLUE + "Player / limbo / disk limbo info for '" + arguments.get(0) + "'");
|
||||
new InfoDisplayer(sender, player, memoryLimbo, diskLimbo)
|
||||
.sendEntry("Is op", Player::isOp, LimboPlayer::isOperator)
|
||||
.sendEntry("Walk speed", Player::getWalkSpeed, LimboPlayer::getWalkSpeed)
|
||||
|
@ -30,6 +30,7 @@ import static fr.xephi.authme.command.executable.authme.debug.DebugSectionUtils.
|
||||
import static fr.xephi.authme.data.auth.PlayerAuth.DB_EMAIL_DEFAULT;
|
||||
import static fr.xephi.authme.data.auth.PlayerAuth.DB_LAST_IP_DEFAULT;
|
||||
import static fr.xephi.authme.data.auth.PlayerAuth.DB_LAST_LOGIN_DEFAULT;
|
||||
import static fr.xephi.authme.datasource.SqlDataSourceUtils.getColumnDefaultValue;
|
||||
import static fr.xephi.authme.datasource.SqlDataSourceUtils.isNotNullColumn;
|
||||
import static java.lang.String.format;
|
||||
|
||||
@ -39,6 +40,9 @@ import static java.lang.String.format;
|
||||
*/
|
||||
class MySqlDefaultChanger implements DebugSection {
|
||||
|
||||
private static final String NOT_NULL_SUFFIX = ChatColor.DARK_AQUA + "@" + ChatColor.RESET;
|
||||
private static final String DEFAULT_VALUE_SUFFIX = ChatColor.GOLD + "#" + ChatColor.RESET;
|
||||
|
||||
@Inject
|
||||
private Settings settings;
|
||||
|
||||
@ -59,7 +63,7 @@ class MySqlDefaultChanger implements DebugSection {
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Add or remove the default value of a column for MySQL";
|
||||
return "Add or remove the default value of MySQL columns";
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -76,9 +80,12 @@ class MySqlDefaultChanger implements DebugSection {
|
||||
|
||||
Operation operation = matchToEnum(arguments, 0, Operation.class);
|
||||
Columns column = matchToEnum(arguments, 1, Columns.class);
|
||||
if (operation == null || column == null) {
|
||||
if (operation == Operation.DETAILS) {
|
||||
showColumnDetails(sender);
|
||||
} else if (operation == null || column == null) {
|
||||
displayUsageHints(sender);
|
||||
} else {
|
||||
sender.sendMessage(ChatColor.BLUE + "[AuthMe] MySQL change '" + column + "'");
|
||||
try (Connection con = getConnection(mySql)) {
|
||||
switch (operation) {
|
||||
case ADD:
|
||||
@ -165,44 +172,54 @@ class MySqlDefaultChanger implements DebugSection {
|
||||
+ sender.getName() + "'");
|
||||
}
|
||||
|
||||
private void showColumnDetails(CommandSender sender) {
|
||||
sender.sendMessage(ChatColor.BLUE + "MySQL column details");
|
||||
final String tableName = settings.getProperty(DatabaseSettings.MYSQL_TABLE);
|
||||
try (Connection con = getConnection(mySql)) {
|
||||
final DatabaseMetaData metaData = con.getMetaData();
|
||||
for (Columns col : Columns.values()) {
|
||||
String columnName = settings.getProperty(col.getColumnNameProperty());
|
||||
String isNullText = isNotNullColumn(metaData, tableName, columnName) ? "NOT NULL" : "nullable";
|
||||
Object defaultValue = getColumnDefaultValue(metaData, tableName, columnName);
|
||||
String defaultText = defaultValue == null ? "no default" : "default: '" + defaultValue + "'";
|
||||
sender.sendMessage(formatColumnWithMetadata(col, metaData, tableName)
|
||||
+ " (" + columnName + "): " + isNullText + ", " + defaultText);
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
ConsoleLogger.logException("Failed while showing column details:", e);
|
||||
sender.sendMessage("Failed while showing column details. See log for info");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays sample commands and the list of columns that can be changed.
|
||||
*
|
||||
* @param sender the sender issuing the command
|
||||
*/
|
||||
private void displayUsageHints(CommandSender sender) {
|
||||
sender.sendMessage(ChatColor.BLUE + "MySQL column changer");
|
||||
sender.sendMessage("Adds or removes a NOT NULL constraint for a column.");
|
||||
sender.sendMessage(" Only available for MySQL.");
|
||||
if (mySql == null) {
|
||||
sender.sendMessage("You are currently not using MySQL!");
|
||||
return;
|
||||
}
|
||||
|
||||
sender.sendMessage("Examples: add a NOT NULL constraint with");
|
||||
sender.sendMessage(" /authme debug mysqldef add <column>");
|
||||
sender.sendMessage("Remove a NOT NULL constraint with");
|
||||
sender.sendMessage(" /authme debug mysqldef remove <column>");
|
||||
sender.sendMessage("Remove one with /authme debug mysqldef remove <column>");
|
||||
|
||||
// Note ljacqu 20171015: Intentionally avoid green & red as to avoid suggesting that one state is good or bad
|
||||
sender.sendMessage("Available columns: " + constructColoredColumnList());
|
||||
sender.sendMessage(" where " + ChatColor.DARK_AQUA + "blue " + ChatColor.RESET
|
||||
+ "is currently not-null, and " + ChatColor.GOLD + "gold " + ChatColor.RESET + "is null");
|
||||
sender.sendMessage("Available columns: " + constructColumnListWithMetadata());
|
||||
sender.sendMessage(" " + NOT_NULL_SUFFIX + ": not-null, " + DEFAULT_VALUE_SUFFIX
|
||||
+ ": has default. See /authme debug mysqldef details");
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list of {@link Columns} we can toggle, colored by their current not-null status
|
||||
* @return list of {@link Columns} we can toggle with suffixes indicating their NOT NULL and default value status
|
||||
*/
|
||||
private String constructColoredColumnList() {
|
||||
private String constructColumnListWithMetadata() {
|
||||
try (Connection con = getConnection(mySql)) {
|
||||
final DatabaseMetaData metaData = con.getMetaData();
|
||||
final String tableName = settings.getProperty(DatabaseSettings.MYSQL_TABLE);
|
||||
|
||||
List<String> formattedColumns = new ArrayList<>(Columns.values().length);
|
||||
for (Columns col : Columns.values()) {
|
||||
String columnName = settings.getProperty(col.getColumnNameProperty());
|
||||
boolean isNotNull = isNotNullColumn(metaData, tableName, columnName);
|
||||
String formattedColumn = (isNotNull ? ChatColor.DARK_AQUA : ChatColor.GOLD) + col.name().toLowerCase();
|
||||
formattedColumns.add(formattedColumn);
|
||||
formattedColumns.add(formatColumnWithMetadata(col, metaData, tableName));
|
||||
}
|
||||
return String.join(ChatColor.RESET + ", ", formattedColumns);
|
||||
} catch (SQLException e) {
|
||||
@ -211,6 +228,16 @@ class MySqlDefaultChanger implements DebugSection {
|
||||
}
|
||||
}
|
||||
|
||||
private String formatColumnWithMetadata(Columns column, DatabaseMetaData metaData,
|
||||
String tableName) throws SQLException {
|
||||
String columnName = settings.getProperty(column.getColumnNameProperty());
|
||||
boolean isNotNull = isNotNullColumn(metaData, tableName, columnName);
|
||||
boolean hasDefaultValue = getColumnDefaultValue(metaData, tableName, columnName) != null;
|
||||
return column.name()
|
||||
+ (isNotNull ? NOT_NULL_SUFFIX : "")
|
||||
+ (hasDefaultValue ? DEFAULT_VALUE_SUFFIX : "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Connection object from the MySQL data source.
|
||||
*
|
||||
@ -239,7 +266,7 @@ class MySqlDefaultChanger implements DebugSection {
|
||||
}
|
||||
|
||||
private enum Operation {
|
||||
ADD, REMOVE
|
||||
ADD, REMOVE, DETAILS
|
||||
}
|
||||
|
||||
/** MySQL columns which can be toggled between being NOT NULL and allowing NULL values. */
|
||||
|
@ -4,6 +4,7 @@ import fr.xephi.authme.permission.DebugSectionPermissions;
|
||||
import fr.xephi.authme.permission.PermissionNode;
|
||||
import fr.xephi.authme.permission.PermissionsManager;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
@ -30,6 +31,7 @@ class PermissionGroups implements DebugSection {
|
||||
|
||||
@Override
|
||||
public void execute(CommandSender sender, List<String> arguments) {
|
||||
sender.sendMessage(ChatColor.BLUE + "AuthMe permission groups");
|
||||
String name = arguments.isEmpty() ? sender.getName() : arguments.get(0);
|
||||
Player player = Bukkit.getPlayer(name);
|
||||
if (player == null) {
|
||||
|
@ -39,6 +39,7 @@ class PlayerAuthViewer implements DebugSection {
|
||||
@Override
|
||||
public void execute(CommandSender sender, List<String> arguments) {
|
||||
if (arguments.isEmpty()) {
|
||||
sender.sendMessage(ChatColor.BLUE + "AuthMe database viewer");
|
||||
sender.sendMessage("Enter player name to view his data in the database.");
|
||||
sender.sendMessage("Example: /authme debug db Bobby");
|
||||
return;
|
||||
@ -46,6 +47,7 @@ class PlayerAuthViewer implements DebugSection {
|
||||
|
||||
PlayerAuth auth = dataSource.getAuth(arguments.get(0));
|
||||
if (auth == null) {
|
||||
sender.sendMessage(ChatColor.BLUE + "AuthMe database viewer");
|
||||
sender.sendMessage("No record exists for '" + arguments.get(0) + "'");
|
||||
} else {
|
||||
displayAuthToSender(auth, sender);
|
||||
@ -64,7 +66,7 @@ class PlayerAuthViewer implements DebugSection {
|
||||
* @param sender the sender to send the messages to
|
||||
*/
|
||||
private void displayAuthToSender(PlayerAuth auth, CommandSender sender) {
|
||||
sender.sendMessage(ChatColor.GOLD + "[AuthMe] Player " + auth.getNickname() + " / " + auth.getRealName());
|
||||
sender.sendMessage(ChatColor.BLUE + "[AuthMe] Player " + auth.getNickname() + " / " + auth.getRealName());
|
||||
sender.sendMessage("Email: " + auth.getEmail() + ". IP: " + auth.getLastIp() + ". Group: " + auth.getGroupId());
|
||||
sender.sendMessage("Quit location: "
|
||||
+ formatLocation(auth.getQuitLocX(), auth.getQuitLocY(), auth.getQuitLocZ(), auth.getWorld()));
|
||||
|
@ -6,6 +6,7 @@ import fr.xephi.authme.service.BukkitService;
|
||||
import fr.xephi.authme.settings.Settings;
|
||||
import fr.xephi.authme.settings.SpawnLoader;
|
||||
import fr.xephi.authme.settings.properties.RestrictionSettings;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
@ -42,6 +43,7 @@ class SpawnLocationViewer implements DebugSection {
|
||||
|
||||
@Override
|
||||
public void execute(CommandSender sender, List<String> arguments) {
|
||||
sender.sendMessage(ChatColor.BLUE + "AuthMe spawn location viewer");
|
||||
if (arguments.isEmpty()) {
|
||||
showGeneralInfo(sender);
|
||||
} else if ("?".equals(arguments.get(0))) {
|
||||
|
@ -44,6 +44,7 @@ class TestEmailSender implements DebugSection {
|
||||
|
||||
@Override
|
||||
public void execute(CommandSender sender, List<String> arguments) {
|
||||
sender.sendMessage(ChatColor.BLUE + "AuthMe test email sender");
|
||||
if (!sendMailSsl.hasAllInformation()) {
|
||||
sender.sendMessage(ChatColor.RED + "You haven't set all required configurations in config.yml "
|
||||
+ "for sending emails. Please check your config.yml");
|
||||
|
@ -12,6 +12,7 @@ import fr.xephi.authme.initialization.HasCleanup;
|
||||
import fr.xephi.authme.initialization.Reloadable;
|
||||
import fr.xephi.authme.initialization.SettingsDependent;
|
||||
import fr.xephi.authme.initialization.factory.SingletonStore;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@ -80,7 +81,9 @@ public class DataStatisticsTest {
|
||||
ArgumentCaptor<String> stringCaptor = ArgumentCaptor.forClass(String.class);
|
||||
verify(sender, atLeastOnce()).sendMessage(stringCaptor.capture());
|
||||
assertThat(stringCaptor.getAllValues(), containsInAnyOrder(
|
||||
"Singleton Java classes: 7 (Reloadable: 4 / SettingsDependent: 3 / HasCleanup: 2)",
|
||||
ChatColor.BLUE + "AuthMe statistics",
|
||||
"Singleton Java classes: 7",
|
||||
"(Reloadable: 4 / SettingsDependent: 3 / HasCleanup: 2)",
|
||||
"LimboPlayers in memory: 1",
|
||||
"Total players in DB: 219",
|
||||
"PlayerCache size: 12 (= logged in players)"));
|
||||
|
@ -4,6 +4,7 @@ import fr.xephi.authme.initialization.factory.Factory;
|
||||
import fr.xephi.authme.permission.DebugSectionPermissions;
|
||||
import fr.xephi.authme.permission.PermissionNode;
|
||||
import fr.xephi.authme.permission.PermissionsManager;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@ -85,11 +86,12 @@ public class DebugCommandTest {
|
||||
verify(permissionsManager, atLeast(MIN_DEBUG_SECTIONS)).hasPermission(eq(sender), any(DebugSectionPermissions.class));
|
||||
|
||||
ArgumentCaptor<String> strCaptor = ArgumentCaptor.forClass(String.class);
|
||||
verify(sender, times(3)).sendMessage(strCaptor.capture());
|
||||
verify(sender, times(4)).sendMessage(strCaptor.capture());
|
||||
assertThat(strCaptor.getAllValues(), contains(
|
||||
containsString("Sections available to you"),
|
||||
equalTo(ChatColor.BLUE + "AuthMe debug utils"),
|
||||
equalTo("Sections available to you:"),
|
||||
containsString("stats: Outputs general data statistics"),
|
||||
containsString("valid: Check if email / password is valid")));
|
||||
containsString("valid: Checks if your config.yml allows a password / email")));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -106,8 +108,9 @@ public class DebugCommandTest {
|
||||
verify(permissionsManager, atLeast(MIN_DEBUG_SECTIONS)).hasPermission(eq(sender), any(DebugSectionPermissions.class));
|
||||
|
||||
ArgumentCaptor<String> strCaptor = ArgumentCaptor.forClass(String.class);
|
||||
verify(sender, times(2)).sendMessage(strCaptor.capture());
|
||||
verify(sender, times(3)).sendMessage(strCaptor.capture());
|
||||
assertThat(strCaptor.getAllValues(), contains(
|
||||
equalTo(ChatColor.BLUE + "AuthMe debug utils"),
|
||||
equalTo("Sections available to you:"),
|
||||
containsString("You don't have permission to view any debug section")));
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user