Skip to content

Commit

Permalink
Make certain !getvalue and !setvalue fields available to admins
Browse files Browse the repository at this point in the history
Fixes #567
  • Loading branch information
RussellLVP committed May 15, 2020
1 parent fef63b3 commit f030d24
Show file tree
Hide file tree
Showing 5 changed files with 108 additions and 44 deletions.
24 changes: 15 additions & 9 deletions javascript/features/account/account_database.js
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,21 @@ export class AccountDatabase {
return true;
}

// Returns the fields which may be modified by administrators.
getSupportedFieldsForAdministrators() {
return [
'custom_color',
'death_message',
'money_bank',
'money_bounty',
'money_cash',
'money_debt',
'money_spawn',
'skin_id',
'validated',
];
}

// Returns which fields are supported by the !supported, !getvalue and !setvalue commands. This
// is a hardcoded list because we only want to support a sub-set of the database column data.
getSupportedFields() {
Expand All @@ -429,7 +444,6 @@ export class AccountDatabase {
last_ip: { table: 'users_mutable', type: AccountDatabase.kTypeCustom },
last_seen: { table: 'users_mutable', type: AccountDatabase.kTypeCustom },
level: { table: 'users', type: AccountDatabase.kTypeCustom },
money_bank_type: { table: 'users_mutable', type: AccountDatabase.kTypeCustom },
money_bank: { table: 'users_mutable', type: AccountDatabase.kTypeNumber },
money_bounty: { table: 'users_mutable', type: AccountDatabase.kTypeNumber },
money_cash: { table: 'users_mutable', type: AccountDatabase.kTypeNumber },
Expand Down Expand Up @@ -550,7 +564,6 @@ export class AccountDatabase {
switch (fieldName) {
case 'last_seen':
case 'level':
case 'money_bank_type':
return value;

case 'custom_color': {
Expand Down Expand Up @@ -634,13 +647,6 @@ export class AccountDatabase {

processedValue = value;
break;

case 'money_bank_type':
if (!['Normal', 'Premier'].includes(value))
throw new Error(`"${value}" is not a valid bank account type.`);

processedValue = value;
break;

default:
throw new Error(`Formatting for ${column} has not been implemented.`);
Expand Down
38 changes: 12 additions & 26 deletions javascript/features/account/account_database.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -178,32 +178,6 @@ describe('AccountDatabase', it => {
assert.equal(instance.updatedValue, 'Player');
});

it('should be able to format and update bank account types', async (assert) => {
const instance = new MockAccountDatabase();

const value = await instance.getPlayerField('[BB]Ricky92', 'money_bank_type');
assert.isTrue(typeof value === 'string');
assert.equal(value, 'Premier');

const invalidValues = ['normal', 'SuperAccount', 'premier'];
for (const invalidValue of invalidValues) {
try {
await instance.updatePlayerField('nickname', 'money_bank_type', invalidValue);
assert.notReached();

} catch (exception) {
assert.includes(exception.message, 'not a valid bank account type');
}
}

const result = await instance.updatePlayerField('nickname', 'money_bank_type', 'Normal');
assert.isTrue(typeof result === 'string');
assert.strictEqual(result, 'Normal');

assert.isTrue(typeof instance.updatedValue === 'string');
assert.equal(instance.updatedValue, 'Normal');
});

it('should be able to format and update last seen IP addresses', async (assert) => {
const instance = new MockAccountDatabase();

Expand Down Expand Up @@ -365,4 +339,16 @@ describe('AccountDatabase', it => {
assert.equal(instance.nameMutation.nickname, '[BB]Ricky92');
assert.equal(instance.nameMutation.newNickname, 'NewNick');
});

it('should only list valid admin-changeable fields for !getvalue & co.', assert => {
const instance = new MockAccountDatabase();

const administratorFields = instance.getSupportedFieldsForAdministrators();
const fields = instance.getSupportedFields();

// If this check fails, one of the fields returned from getSupportedFieldsForAdministrators
// is not a valid, supported database field anymore. Make sure it's aligned.
for (const fieldName of administratorFields)
assert.isTrue(fieldName in fields);
});
});
28 changes: 22 additions & 6 deletions javascript/features/account/account_nuwani_commands.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,20 +82,20 @@ export class AccountNuwaniCommands {

// !supported
this.commandManager_.buildCommand('supported')
.restrict(Player.LEVEL_MANAGEMENT)
.restrict(Player.LEVEL_ADMINISTRATOR)
.build(AccountNuwaniCommands.prototype.onSupportedCommand.bind(this));

// !getvalue [field]
this.commandManager_.buildCommand('getvalue')
.restrict(Player.LEVEL_MANAGEMENT)
.restrict(Player.LEVEL_ADMINISTRATOR)
.parameters([
{ name: 'nickname', type: CommandBuilder.WORD_PARAMETER },
{ name: 'field', type: CommandBuilder.WORD_PARAMETER }])
.build(AccountNuwaniCommands.prototype.onGetValueCommand.bind(this));

// !setvalue [field] [value]
this.commandManager_.buildCommand('setvalue')
.restrict(Player.LEVEL_MANAGEMENT)
.restrict(Player.LEVEL_ADMINISTRATOR)
.parameters([
{ name: 'nickname', type: CommandBuilder.WORD_PARAMETER },
{ name: 'field', type: CommandBuilder.WORD_PARAMETER },
Expand Down Expand Up @@ -255,14 +255,26 @@ export class AccountNuwaniCommands {
// Displays a list of the supported fields in the player account data store that can be read and
// updated by the commands. Type and table information is omitted.
onSupportedCommand(context) {
const supported = Object.keys(this.database_.getSupportedFields()).sort();
let supported = null;

if (context.level === Player.LEVEL_ADMINISTRATOR)
supported = this.database_.getSupportedFieldsForAdministrators().sort();
else
supported = Object.keys(this.database_.getSupportedFields()).sort();

context.respond('5Supported fields: ' + supported.join(', '));
}

// !getvalue [nickname] [field]
//
// Displays the given |field| from |nickname|'s account data. Only available to management.
async onGetValueCommand(context, nickname, field) {
const administratorFields = this.database_.getSupportedFieldsForAdministrators();
if (context.level != Player.LEVEL_MANAGEMENT && !administratorFields.includes(field)) {
context.respond(`4Error: Sorry, the "${field}" field is not accessible to you.`);
return;
}

try {
const value = await this.database_.getPlayerField(nickname, field);
context.respond(`5Value of "${field}": ${value}`);
Expand All @@ -281,6 +293,12 @@ export class AccountNuwaniCommands {
return;
}

const administratorFields = this.database_.getSupportedFieldsForAdministrators();
if (context.level != Player.LEVEL_MANAGEMENT && !administratorFields.includes(field)) {
context.respond(`4Error: Sorry, the "${field}" field is not accessible to you.`);
return;
}

try {
const writtenValue = await this.database_.updatePlayerField(nickname, field, value);
context.respond(`3Success: The value has been updated to "${writtenValue}".`);
Expand Down Expand Up @@ -342,8 +360,6 @@ export class AccountNuwaniCommands {
}
}



formattedPlayers.push(
prefix + color + info.name + (color ? '' : '') + (info.minimized ? '' : ''));
}
Expand Down
60 changes: 57 additions & 3 deletions javascript/features/account/account_nuwani_commands.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ describe('AccountNuwaniCommands', (it, beforeEach, afterEach) => {
bot.dispose();
});

const kFieldAccessibleByAdmins = 'skin_id';
const kFieldNotAccessibleByAdmins = 'online_time';

it('should make it possible to add an alias to an account', async (assert) => {
bot.setUserModesInEchoChannelForTesting(kCommandSourceUsername, 'h');

Expand Down Expand Up @@ -182,15 +185,28 @@ describe('AccountNuwaniCommands', (it, beforeEach, afterEach) => {
});

it('should be able to display a list of supported database fields', async (assert) => {
bot.setUserModesInEchoChannelForTesting(kCommandSourceUsername, 'h');

const adminResult = await issueCommand(bot, commandManager, {
source: kCommandSource,
command: '!supported',
});

assert.equal(adminResult.length, 1);
assert.includes(adminResult[0], 'Supported fields');
assert.includes(adminResult[0], kFieldAccessibleByAdmins);
assert.doesNotInclude(adminResult[0], kFieldNotAccessibleByAdmins);

bot.setUserModesInEchoChannelForTesting(kCommandSourceUsername, 'a');

const result = await issueCommand(bot, commandManager, {
const managementResult = await issueCommand(bot, commandManager, {
source: kCommandSource,
command: '!supported',
});

assert.equal(result.length, 1);
assert.includes(result[0], 'Supported fields');
assert.equal(managementResult.length, 1);
assert.includes(managementResult[0], 'Supported fields');
assert.includes(managementResult[0], kFieldNotAccessibleByAdmins);
});

it('should be able to get individual account values for a player', async (assert) => {
Expand Down Expand Up @@ -269,6 +285,44 @@ describe('AccountNuwaniCommands', (it, beforeEach, afterEach) => {
assert.includes(result[0], 'Success');
});

it('should enable administrators to update certain fields as well', async (assert) => {
bot.setUserModesInEchoChannelForTesting(kCommandSourceUsername, 'h');

const noAccessGetResult = await issueCommand(bot, commandManager, {
source: kCommandSource,
command: '!getvalue [BB]Ricky92 ' + kFieldNotAccessibleByAdmins,
});

assert.equal(noAccessGetResult.length, 1);
assert.includes(noAccessGetResult[0], 'Error');
assert.includes(noAccessGetResult[0], 'is not accessible');

const getResult = await issueCommand(bot, commandManager, {
source: kCommandSource,
command: '!getvalue [BB]Ricky92 ' + kFieldAccessibleByAdmins,
});

assert.equal(getResult.length, 1);
assert.doesNotInclude(getResult[0], 'Error');

const setResult = await issueCommand(bot, commandManager, {
source: kCommandSource,
command: '!setvalue [BB]Ricky92 ' + kFieldAccessibleByAdmins + ' 1234',
});

assert.equal(setResult.length, 1);
assert.doesNotInclude(setResult[0], 'Error');

const noAccessSetResult = await issueCommand(bot, commandManager, {
source: kCommandSource,
command: '!setvalue [BB]Ricky92 ' + kFieldNotAccessibleByAdmins + ' 1234',
});

assert.equal(noAccessSetResult.length, 1);
assert.includes(noAccessSetResult[0], 'Error');
assert.includes(noAccessSetResult[0], 'is not accessible');
});

it('should be able to list all in-game players', async (assert) => {
let result;

Expand Down
2 changes: 2 additions & 0 deletions javascript/features/account/test/mock_account_database.js
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,8 @@ export class MockAccountDatabase extends AccountDatabase {
return 623925203; // 37.48.87.211
case 'last_seen':
return '2019-12-24 12:44:41';
case 'skin_id':
return 121;
default:
throw new Error('Field not defined for testing: ' + fieldName);
}
Expand Down

0 comments on commit f030d24

Please sign in to comment.