Merge branch 'master' of gitlab.libraryofcode.org:engineering/cloudservices
commit
e934d6d398
|
@ -5,6 +5,7 @@ import nodemailer from 'nodemailer';
|
|||
import { Message, PrivateChannel, Member, User } from 'eris';
|
||||
import uuid from 'uuid/v4';
|
||||
import moment from 'moment';
|
||||
import fs from 'fs';
|
||||
import { Client } from '..';
|
||||
import { Command, RichEmbed } from '.';
|
||||
import { ModerationInterface, AccountInterface } from '../models';
|
||||
|
@ -195,9 +196,13 @@ export default class Util {
|
|||
|
||||
/**
|
||||
* @param type `0` - Create
|
||||
*
|
||||
* `1` - Warn
|
||||
*
|
||||
* `2` - Lock
|
||||
*
|
||||
* `3` - Unlock
|
||||
*
|
||||
* `4` - Delete
|
||||
*/
|
||||
public async createModerationLog(user: string, moderator: Member|User, type: number, reason?: string, duration?: number): Promise<ModerationInterface> {
|
||||
|
@ -251,4 +256,12 @@ export default class Util {
|
|||
|
||||
return Promise.resolve(log);
|
||||
}
|
||||
|
||||
public getAcctHash(username: string) {
|
||||
try {
|
||||
return fs.readFileSync(`/home/${username}/.securesign/auth`).toString();
|
||||
} catch (error) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -53,10 +53,10 @@ export default class Help extends Command {
|
|||
if (allowedUsers) { allowedUsers = `**Users:** ${allowedUsers}`; perms.push(allowedUsers); }
|
||||
const displayedPerms = perms.length ? `\n**Permissions:**\n${perms.join('\n')}` : '';
|
||||
const aliases = cmd.aliases.length ? `\n**Aliases:** ${cmd.aliases.map((alias) => `${this.client.config.prefix}${cmd.parentName}${alias}`).join(', ')}` : '';
|
||||
const subcommands = cmd.subcommands.size ? `\n**Subcommands:** ${cmd.subcommands.map((s) => s.parentName || `${cmd.name} ${s.name}`).join(', ')}` : '';
|
||||
const subcommands = cmd.subcommands.size ? `\n**Subcommands:** ${cmd.subcommands.map((s) => `${cmd.name} ${s.name}`).join(', ')}` : '';
|
||||
const embed = new RichEmbed();
|
||||
embed.setTimestamp(); embed.setFooter(`Requested by ${message.author.username}#${message.author.discriminator}`, message.author.avatarURL);
|
||||
embed.setTitle(`${this.client.config.prefix}${cmd.parentName}${cmd.name}`); embed.setAuthor(`${this.client.user.username}#${this.client.user.discriminator}`, this.client.user.avatarURL);
|
||||
embed.setTitle(`${this.client.config.prefix}${cmd.parentName ? `${cmd.parentName}${cmd.name}` : cmd.name}`); embed.setAuthor(`${this.client.user.username}#${this.client.user.discriminator}`, this.client.user.avatarURL);
|
||||
const description = `**Description**: ${cmd.description}\n**Usage:** ${cmd.usage}${aliases}${displayedPerms}${subcommands}`;
|
||||
embed.setDescription(description);
|
||||
// @ts-ignore
|
||||
|
|
|
@ -14,6 +14,7 @@ export default class Pull extends Command {
|
|||
|
||||
public async run(message: Message) {
|
||||
try {
|
||||
this.client.updating = true;
|
||||
const updateMessage = await message.channel.createMessage(`${this.client.stores.emojis.loading} ***Fetching latest commit...***\n\`\`\`sh\ngit pull\n\`\`\``);
|
||||
let pull: string;
|
||||
|
||||
|
@ -22,16 +23,19 @@ export default class Pull extends Command {
|
|||
} catch (error) {
|
||||
const updatedMessage = updateMessage.content.replace(`${this.client.stores.emojis.loading} ***Fetching latest commit...***`, `${this.client.stores.emojis.error} ***Could not fetch latest commit***`)
|
||||
.replace(/```$/, `${error.message}\n\`\`\``);
|
||||
this.client.updating = false;
|
||||
return updateMessage.edit(updatedMessage);
|
||||
}
|
||||
if (pull.includes('Already up to date')) {
|
||||
const updatedMessage = updateMessage.content.replace(`${this.client.stores.emojis.loading} ***Fetching latest commit...***`, `${this.client.stores.emojis.success} ***No updates available***`)
|
||||
.replace(/```$/, `${pull}\n\`\`\``);
|
||||
this.client.updating = false;
|
||||
return updateMessage.edit(updatedMessage);
|
||||
}
|
||||
if (!pull.includes('origin/master')) {
|
||||
const updatedMessage = updateMessage.content.replace(`${this.client.stores.emojis.loading} ***Fetching latest commit...***`, `${this.client.stores.emojis.error} ***Unexpected git output***`)
|
||||
.replace(/```$/, `${pull}\n\`\`\``);
|
||||
this.client.updating = false;
|
||||
return updateMessage.edit(updatedMessage);
|
||||
}
|
||||
const continueMessage = updateMessage.content.replace(`${this.client.stores.emojis.loading} ***Fetching latest commit...***`, `${this.client.stores.emojis.success} ***Pulled latest commit***\n${this.client.stores.emojis.loading} ***Reinstalling dependencies...***`)
|
||||
|
@ -43,6 +47,7 @@ export default class Pull extends Command {
|
|||
try {
|
||||
install = await this.client.util.exec('yarn install');
|
||||
} catch (error) {
|
||||
this.client.updating = false;
|
||||
const updatedMessage = passedPull.content.replace(`${this.client.stores.emojis.loading} ***Reinstalling dependencies...***`, `${this.client.stores.emojis.error} ***Failed to reinstall dependencies***`)
|
||||
.replace(/```$/, `${error.message}\n\`\`\``);
|
||||
return updateMessage.edit(updatedMessage);
|
||||
|
@ -59,6 +64,7 @@ export default class Pull extends Command {
|
|||
} else {
|
||||
const updatedMessage = passedPull.content.replace(`${this.client.stores.emojis.loading} ***Reinstalling dependencies...***`, `${this.client.stores.emojis.error} ***Unexpected yarn install output***`)
|
||||
.replace(/```$/, `${pull}\n\`\`\``);
|
||||
this.client.updating = false;
|
||||
return updateMessage.edit(updatedMessage);
|
||||
}
|
||||
|
||||
|
@ -68,13 +74,15 @@ export default class Pull extends Command {
|
|||
} catch (error) {
|
||||
const updatedMessage = updatedPackages.content.replace(`${this.client.stores.emojis.loading} ***Rebuilding files...***`, `${this.client.stores.emojis.error} ***Failed to rebuild files***`)
|
||||
.replace(/```$/, `${error.message}\n\`\`\``);
|
||||
this.client.updating = false;
|
||||
return updateMessage.edit(updatedMessage);
|
||||
}
|
||||
const finalMessage = updatedPackages.content.replace(`${this.client.stores.emojis.loading} ***Rebuilding files...***`, `${this.client.stores.emojis.success} ***Files rebuilt***`)
|
||||
.replace(/```$/, `${build}\n\`\`\``);
|
||||
|
||||
this.client.updating = false;
|
||||
return updateMessage.edit(finalMessage);
|
||||
} catch (error) {
|
||||
this.client.updating = false;
|
||||
return this.client.util.handleError(error, message, this);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import Build from './securesign_build';
|
|||
import Init from './securesign_init';
|
||||
import Account from './securesign_account';
|
||||
import ActivateKey from './securesign_activatekey';
|
||||
import CreateCrt from './securesign_createcrt';
|
||||
|
||||
export default class SecureSign extends Command {
|
||||
constructor(client: Client) {
|
||||
|
@ -13,7 +14,7 @@ export default class SecureSign extends Command {
|
|||
this.description = 'Runs SecureSign CLI commands';
|
||||
this.usage = `Run ${this.client.config.prefix}${this.name} [subcommand] for usage information`;
|
||||
this.aliases = ['ss'];
|
||||
this.subcmds = [Build, Init, Account, ActivateKey];
|
||||
this.subcmds = [Build, Init, Account, ActivateKey, CreateCrt];
|
||||
this.enabled = true;
|
||||
}
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ export default class SecureSign_Account extends Command {
|
|||
embed.setTitle(title);
|
||||
embed.setDescription(description);
|
||||
embed.setAuthor(this.client.user.username, this.client.user.avatarURL);
|
||||
embed.setFooter(`Requested by ${message.member.username}#${message.member.discriminator}`, message.member.avatarURL);
|
||||
embed.setFooter(`Requested by ${message.author.username}#${message.author.discriminator}`, message.author.avatarURL);
|
||||
|
||||
// @ts-ignore
|
||||
return msg.edit({ content, embed });
|
||||
|
|
|
@ -19,17 +19,18 @@ export default class SecureSign_ActivateKey extends Command {
|
|||
if (!account) return message.channel.createMessage(`${this.client.stores.emojis.error} ***Account not found***`);
|
||||
if (!account.hash) return message.channel.createMessage(`${this.client.stores.emojis.error} ***Account not initialized***`);
|
||||
const msg = await message.channel.createMessage(`${this.client.stores.emojis.loading} ***Activating key...***`);
|
||||
const hash = this.client.util.getAcctHash(account.username);
|
||||
try {
|
||||
await axios({
|
||||
method: 'POST',
|
||||
url: 'https://api.securesign.org/account/keys/activation',
|
||||
headers: { Authorization: account.hash, 'Content-Type': 'application/json' },
|
||||
headers: { Authorization: hash, 'Content-Type': 'application/json' },
|
||||
data: JSON.stringify({ key: args[0] }),
|
||||
});
|
||||
} catch (error) {
|
||||
const { code } = error.response.data;
|
||||
if (code === 1001) {
|
||||
await this.client.db.Account.updateOne({ hash: account.hash }, { $set: { hash: null } });
|
||||
await this.client.db.Account.updateOne({ userID: account.userID }, { $set: { hash: false } });
|
||||
this.client.getDMChannel(account.userID).then((channel) => channel.createMessage('Your SecureSign password has been reset - please reinitialize your SecureSign account')).catch();
|
||||
return msg.edit(`${this.client.stores.emojis.error} ***Authentication failed***`);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
import { Message, PrivateChannel, TextChannel } from 'eris';
|
||||
import axios from 'axios';
|
||||
import { Client } from '..';
|
||||
import { Command } from '../class';
|
||||
|
||||
export default class SecureSign_Init extends Command {
|
||||
constructor(client: Client) {
|
||||
super(client);
|
||||
this.name = 'createcrt';
|
||||
this.description = 'Creates a new certificate';
|
||||
this.usage = `${this.client.config.prefix}securesign createcrt [-s sign] [-c class] [-m digest]\n\`sign\`: Sign type (ecc/rsa)\n\`class\`: Certificate Class (1/2/3)\n\`digest\`: SHA Digest (256/384/512)`;
|
||||
this.enabled = true;
|
||||
this.guildOnly = false;
|
||||
}
|
||||
|
||||
public async run(message: Message, args: string[]) {
|
||||
try {
|
||||
const account = await this.client.db.Account.findOne({ userID: message.author.id });
|
||||
if (!account) return message.channel.createMessage(`${this.client.stores.emojis.error} ***Account not found***`);
|
||||
if (!account.hash) return message.channel.createMessage(`${this.client.stores.emojis.error} ***Account not initialized***`);
|
||||
|
||||
// @ts-ignore
|
||||
const options: { s?: string, c?: string, m?: string } = args.length ? Object.fromEntries(` ${args.join(' ')}`.split(' -').filter((a) => a).map((a) => a.split(' '))) : {}; // eslint-disable-line
|
||||
if (options.s && options.s.toLowerCase() !== 'ecc' && options.s.toLowerCase() !== 'rsa') return message.channel.createMessage(`${this.client.stores.emojis.error} ***Invalid signing type, choose between \`ecc\` or \`rsa\``);
|
||||
if (options.c && (!Number(options.c) || Number(options.c) < 1 || Number(options.c) > 3)) return message.channel.createMessage(`${this.client.stores.emojis.error} ***Invalid class selected, choose between Class \`1\`, \`2\` or \`3\``);
|
||||
if (options.m && (!Number(options.m) || (options.m !== '256' && options.m !== '384' && options.m !== '512'))) return message.channel.createMessage(`${this.client.stores.emojis.error} ***Invalid SHA Digest selected, choose between \`256\`, \`384\` or \`512\``);
|
||||
|
||||
const msg = await message.channel.createMessage(`${this.client.stores.emojis.loading} ***Creating certificate...***`);
|
||||
const hash = this.client.util.getAcctHash(account.username);
|
||||
|
||||
// Check if they can generate certificate
|
||||
try {
|
||||
const { data } = await axios({
|
||||
method: 'GET',
|
||||
url: 'https://api.securesign.org/account/details',
|
||||
headers: { Authorization: hash, 'Content-Type': 'application/json' },
|
||||
});
|
||||
|
||||
const { total, allowed } = data.message;
|
||||
if (total >= allowed) return msg.edit(`${this.client.stores.emojis.error} ***Not enough certificate allowances - please ask a member of staff to increase this limit from ${total}***`);
|
||||
if (Number(options.c) > data.message.class) return msg.edit(`${this.client.stores.emojis.error} ***Class too low, you are on a class ${data.message.class} account***`);
|
||||
} catch (error) {
|
||||
const { code } = error.response.data;
|
||||
if (code === 1001) {
|
||||
await this.client.db.Account.updateOne({ userID: account.userID }, { $set: { hash: false } });
|
||||
this.client.getDMChannel(account.userID).then((channel) => channel.createMessage('Your SecureSign password has been reset - please reinitialize your SecureSign account')).catch();
|
||||
return msg.edit(`${this.client.stores.emojis.error} ***Authentication failed***`);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
||||
const execoptions = `${options.s ? ` -s ${options.s}` : ''}${options.c ? ` -c ${options.c}` : ''}${options.m ? ` -m ${options.m}` : ''}`;
|
||||
const cmd = `sudo -H -u ${account.username} bash -c 'securesign-canary createcrt${execoptions}'`;
|
||||
|
||||
const exec = await this.client.util.exec(cmd);
|
||||
if (!exec.replace(/^\s+|\s+$/g, '').endsWith('Successfully wrote certificate.')) throw new Error(`Certificate generation did not complete successfully:\n${cmd}`);
|
||||
|
||||
return msg.edit(`${this.client.stores.emojis.success} ***Successfully created certificate***`);
|
||||
} catch (error) {
|
||||
return this.client.util.handleError(error, message, this);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -47,7 +47,7 @@ export default class SecureSign_Init extends Command {
|
|||
}
|
||||
const init = await this.client.util.exec(`sudo -H -u ${account.username} bash -c 'securesign-canary init -a ${args[0]}'`);
|
||||
if (!init.replace(/^\s+|\s+$/g, '').endsWith('Initialization sequence completed.')) throw new Error(`Account initialization did not complete successfully:\n${init}`);
|
||||
await this.client.db.Account.updateOne({ userID: message.author.id }, { $set: { hash: args[0] } });
|
||||
await this.client.db.Account.updateOne({ userID: message.author.id }, { $set: { hash: true } });
|
||||
return msg.edit(`${this.client.stores.emojis.success} ***Account initialized***`);
|
||||
} catch (error) {
|
||||
return this.client.util.handleError(error, message, this);
|
||||
|
|
|
@ -7,8 +7,9 @@ export default function checkSS(client: Client) {
|
|||
try {
|
||||
const accounts = await client.db.Account.find();
|
||||
const hashes = accounts.filter((h) => h.hash);
|
||||
for (const { hash, userID } of hashes) {
|
||||
for (const { userID, username } of hashes) {
|
||||
try {
|
||||
const hash = client.util.getAcctHash(username);
|
||||
await axios({
|
||||
method: 'get',
|
||||
url: 'https://api.securesign.org/account/details',
|
||||
|
@ -17,7 +18,7 @@ export default function checkSS(client: Client) {
|
|||
} catch (error) {
|
||||
const { status } = error.response;
|
||||
if (status === 400 || status === 401 || status === 403 || status === 404) {
|
||||
await client.db.Account.updateOne({ hash }, { $set: { hash: null } });
|
||||
await client.db.Account.updateOne({ userID }, { $set: { hash: false } });
|
||||
client.getDMChannel(userID).then((channel) => channel.createMessage('Your SecureSign password has been reset - please reinitialize your SecureSign account')).catch();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ export interface AccountInterface extends Document {
|
|||
engineer: boolean
|
||||
},
|
||||
root: boolean,
|
||||
hash: string,
|
||||
hash: boolean,
|
||||
salt: string,
|
||||
authTag: Buffer
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ const Account: Schema = new Schema({
|
|||
engineer: Boolean,
|
||||
},
|
||||
root: Boolean,
|
||||
hash: String,
|
||||
hash: Boolean,
|
||||
salt: String,
|
||||
authTag: Buffer,
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue