summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatarJanik Kleinhoff <ilbelkyr@shalture.org>2017-05-08 02:06:38 +0000
committerLibravatarJanik Kleinhoff <ilbelkyr@shalture.org>2017-05-08 02:25:18 +0000
commit7342c7f0e19e15ab3c7ba2133a56393c15989f08 (patch)
tree5e00a9c2a3164bd49d18ded9263f1ea7ae99d7f6
parent62d8fcde1c6dc5be2f535f6fcffa1afd838b62bd (diff)
Move users to DB
Summary: This will automatically take care of migrating the users from users.json; you may delete that file. Note that this removes htpasswd support. We now store (hashed) user passwords in the database. See T19 for rationale. Test Plan: Run this on a testnet for a while, try to break it. Reviewers: ilbelkyr, #antispammeta Reviewed By: ilbelkyr, #antispammeta Tags: #antispammeta, #database Differential Revision: https://dev.antispammeta.net/D2
-rw-r--r--.gitignore1
-rw-r--r--config-default/settings.json2
-rw-r--r--cpanfile3
-rw-r--r--cpanfile.snapshot13
-rw-r--r--lib/ASM/Commander.pm338
-rw-r--r--lib/ASM/Config.pm7
-rw-r--r--lib/ASM/DB.pm2
-rw-r--r--lib/ASM/DB/Result/User.pm34
-rw-r--r--lib/ASM/DB/ResultSet/User.pm23
-rw-r--r--sql/_common/upgrade/3-4/002-convert_users.pl31
-rw-r--r--sql/_source/deploy/4/001-auto-__VERSION.yml91
-rw-r--r--sql/_source/deploy/4/001-auto.yml437
12 files changed, 812 insertions, 170 deletions
diff --git a/.gitignore b/.gitignore
index 78e004a..d3fd471 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,3 +11,4 @@ HTTP_ACCESS*
# DBIx::Class::DeploymentHandler
/sql/*/
!/sql/_source/
+!/sql/_common/
diff --git a/config-default/settings.json b/config-default/settings.json
index c976383..8fd2b24 100644
--- a/config-default/settings.json
+++ b/config-default/settings.json
@@ -29,12 +29,10 @@
"ssl" : "1",
"username" : "MetaBot",
"web" : {
- "groupfile" : "/var/www/.htgroup",
"shortener" : {
"apikey" : "yoursecretkey",
"domain" : "yourls.org",
"secure" : "0"
},
- "userfile" : "/var/www/.htpasswd"
}
}
diff --git a/cpanfile b/cpanfile
index eedbbdf..a01c954 100644
--- a/cpanfile
+++ b/cpanfile
@@ -1,5 +1,3 @@
-requires 'Apache::Htgroup', '1.23';
-requires 'Apache::Htpasswd', '1.8';
requires 'Array::Utils', '0.5';
requires 'autovivification', '0.12';
requires 'Data::UUID', '1.219';
@@ -18,6 +16,7 @@ requires 'String::Interpolate', '0.32';
requires 'Text::LevenshteinXS', '0.03';
requires 'Tie::CPHash', '1.06';
requires 'URI::Escape', '3.31';
+requires 'Const::Fast', '0.014';
requires 'DBI', '1.63';
requires 'DBD::mysql', '4.025';
diff --git a/cpanfile.snapshot b/cpanfile.snapshot
index 5b9438f..4361a65 100644
--- a/cpanfile.snapshot
+++ b/cpanfile.snapshot
@@ -350,6 +350,19 @@ DISTRIBUTIONS
Module::Pluggable::Object 3.6
Test::More 0
perl 5.006
+ Const-Fast-0.014
+ pathname: L/LE/LEONT/Const-Fast-0.014.tar.gz
+ provides:
+ Const::Fast 0.014
+ requirements:
+ Carp 0
+ Module::Build::Tiny 0.021
+ Scalar::Util 0
+ Storable 0
+ Sub::Exporter::Progressive 0.001007
+ perl 5.008
+ strict 0
+ warnings 0
Context-Preserve-0.01
pathname: J/JR/JROCKWAY/Context-Preserve-0.01.tar.gz
provides:
diff --git a/lib/ASM/Commander.pm b/lib/ASM/Commander.pm
index edad532..6de19fd 100644
--- a/lib/ASM/Commander.pm
+++ b/lib/ASM/Commander.pm
@@ -1,6 +1,7 @@
package ASM::Commander;
no autovivification;
+use v5.10;
use warnings;
use strict;
use IO::All;
@@ -8,45 +9,60 @@ use POSIX qw(strftime);
use Data::Dumper;
use URI::Escape;
use ASM::Shortener;
+use Const::Fast;
no if $] >= 5.017011, warnings => 'experimental::smartmatch';
+const my $secret => 'flag_secret';
+const my $hilights => 'flag_hilights';
+const my $admin => 'flag_admin';
+const my $plugin => 'flag_plugin';
+const my $debug => 'flag_debug';
+
+const my %letter_to_flag => (
+ s => $secret,
+ h => $hilights,
+ a => $admin,
+ p => $plugin,
+ d => $debug,
+);
+
+const my %flag_to_letter => reverse(%letter_to_flag);
+
my $cmdtbl = {
'^;wallop' => {
- 'flag' => 'd',
+ 'flag' => $debug,
'cmd' => \&cmd_wallop },
'^;;addwebuser (?<pass>.{6,})' => {
- 'flag' => 's',
+ 'flag' => $secret,
+ 'txn' => 1,
'cmd' => \&cmd_addwebuser },
- '^;delwebuser (?<user>\S+)' => {
- 'flag' => 'a',
- 'cmd' => \&cmd_delwebuser },
'^;teredo (?<ip>\S+)' => {
'cmd' => \&cmd_teredo },
'^;status$' => {
'cmd' => \&cmd_status },
'^;mship (?<nick>\S+)' => {
- 'flag' => 's',
+ 'flag' => $secret,
'cmd' => \&cmd_mship },
'^;source$' => {
'cmd' => \&cmd_source },
'^;monitor (?<chan>\S+) *$' => {
- 'flag' => 's',
+ 'flag' => $secret,
'cmd' => \&cmd_monitor },
'^;monitor (?<chan>\S+) ?(?<switch>yes|no)$' => {
- 'flag' => 'a',
+ 'flag' => $admin,
'cmd' => \&cmd_monitor2 },
'^;suppress (?<chan>\S+) *$' => {
- 'flag' => 's',
+ 'flag' => $secret,
'cmd' => \&cmd_suppress },
'^;unsuppress (?<chan>\S+) *$' => {
- 'flag' => 's',
+ 'flag' => $secret,
'cmd' => \&cmd_unsuppress },
'^;silence (?<chan>\S+) *$' => {
- 'flag' => 's',
+ 'flag' => $secret,
'cmd' => \&cmd_silence },
'^;silence (?<chan>\S+) (?<switch>yes|no) *$' => {
- 'flag' => 'a',
+ 'flag' => $admin,
'cmd' => \&cmd_silence2 },
'^;help$' => {
'cmd' => \&cmd_help },
@@ -57,89 +73,88 @@ my $cmdtbl = {
'^;query (\S+) ?(\S+)?$' => {
'cmd' => \&cmd_query },
'^;investigate (?<nick>\S+) *$' => {
- 'flag' => 's',
+ 'flag' => $secret,
'cmd' => \&cmd_investigate },
'^;investigate2 (?<nick>\S+) ?(?<skip>\d*) *$' => {
- 'flag' => 's',
+ 'flag' => $secret,
'cmd' => \&cmd_investigate2 },
- '^;userx? add (?<account>\S+) (?<flags>\S+)$' => {
- 'flag' => 'a',
- 'cmd' => \&cmd_user_add },
+ '^;userx? (?:add|flags) (?<account>\S+) (?<flags>\S+)$' => {
+ 'flag' => $admin,
+ 'txn' => 1,
+ 'cmd' => \&cmd_user_set_flags },
'^;userx? flags (?<account>\S+) ?$' => {
- 'cmd' => \&cmd_user_flags },
- '^;userx? flags (?<account>\S+) (?<flags>\S+)$' => {
- 'flag' => 'a',
- 'cmd' => \&cmd_user_flags2 },
+ 'cmd' => \&cmd_user_get_flags },
'^;userx? del (?<account>\S+)$' => {
- 'flag' => 'a',
+ 'flag' => $admin,
+ 'txn' => 1,
'cmd' => \&cmd_user_del },
'^;target (?<chan>\S+) (?<nickchan>\S+) ?(?<level>[a-z]*)$' => {
- 'flag' => 'a',
+ 'flag' => $admin,
'cmd' => \&cmd_target },
'^;detarget (?<chan>\S+) (?<nickchan>\S+)' => {
- 'flag' => 'a',
+ 'flag' => $admin,
'cmd' => \&cmd_detarget },
'^;showhilights (?<nick>\S+) *$' => {
- 'flag' => 'h',
+ 'flag' => $hilights,
'cmd' => \&cmd_showhilights },
'^;hilight (?<chan>\S+) (?<nicks>\S+) ?(?<level>[a-z]*)$' => {
- 'flag' => 'h',
+ 'flag' => $hilights,
'cmd' => \&cmd_hilight },
'^;dehilight (?<chan>\S+) (?<nicks>\S+)' => {
- 'flag' => 'h',
+ 'flag' => $hilights,
'cmd' => \&cmd_dehilight },
'^;join (?<chan>\S+)' => {
- 'flag' => 'a',
+ 'flag' => $admin,
'cmd' => \&cmd_join },
'^;part (?<chan>\S+)' => {
- 'flag' => 'a',
+ 'flag' => $admin,
'cmd' => \&cmd_part },
'^;sl (?<string>.+)' => {
- 'flag' => 'd',
+ 'flag' => $debug,
'cmd' => \&cmd_sl },
'^;quit ?(?<reason>.*)' => {
- 'flag' => 'a',
+ 'flag' => $admin,
'cmd' => \&cmd_quit },
'^;ev (?<string>.*)' => {
- 'flag' => 'd',
+ 'flag' => $debug,
'cmd' => \&cmd_ev },
'^;rehash$' => {
- 'flag' => 'a',
+ 'flag' => $admin,
'cmd' => \&cmd_rehash },
'^;restrict (?<type>nick|account|host) (?<who>\S+) (?<mode>\+|-)(?<restriction>[a-z0-9_-]+)$' => {
- 'flag' => 'a',
+ 'flag' => $admin,
'cmd' => \&cmd_restrict },
'^\s*\!ops ?(?<chan>#\S+)? ?(?<reason>.*)' => {
'nohush' => 'nohush',
'cmd' => \&cmd_ops },
'^;blacklist (?<string>.+)' => {
- 'flag' => 's',
+ 'flag' => $secret,
'cmd' => \&cmd_blacklist },
'^;blacklistpcre (?<string>.+)' => {
- 'flag' => 'a',
+ 'flag' => $admin,
'cmd' => \&cmd_blacklistpcre },
'^;unblacklist (?<id>[0-9a-f]+)$' => {
- 'flag' => 's',
+ 'flag' => $secret,
'cmd' => \&cmd_unblacklist },
'^;plugin (?<chan>\S+) (?<risk>\S+) (?<reason>.*)' => {
- 'flag' => 'p',
+ 'flag' => $plugin,
'cmd' => \&cmd_plugin },
'^;sync (?<chan>\S+)' => {
- 'flag' => 'a',
+ 'flag' => $admin,
'cmd' => \&cmd_sync },
'^;ping\s*$' => {
'cmd' => \&cmd_ping },
'^;ping (?<string>\S.*)$' => {
- 'flag' => 's',
+ 'flag' => $secret,
'cmd' => \&cmd_ping2 },
'^;blreason (?<id>[0-9a-f]+) (?<reason>.*)' => {
- 'flag' => 's',
+ 'flag' => $secret,
'cmd' => \&cmd_blreason },
'^;bllookup (?<id>[0-9a-f]+)$' => {
- 'flag' => 's',
+ 'flag' => $secret,
'cmd' => \&cmd_bllookup },
'^;falsematch\b' => {
- 'flag' => 's',
+ 'flag' => $secret,
'cmd' => \&cmd_falsematch },
'^;version$' => {
'cmd' => \&cmd_version },
@@ -174,34 +189,50 @@ sub command {
unless ( (ASM::Util->speak($event->{to}->[0])) ) {
next unless (defined($self->{cmdtbl}->{$command}->{nohush}));
}
- if (defined($self->{cmdtbl}->{$command}->{flag})) { #If the command is restricted,
- if (!defined($acct)) {
- $fail = 1;
- }
- elsif (!defined($::users->{person}->{$acct})) { #make sure the requester has an account
- $fail = 1;
- }
- elsif (!defined($::users->{person}->{$acct}->{flags})) { #make sure the requester has flags defined
- $fail = 1;
- }
- elsif (!(grep {$_ eq $self->{cmdtbl}->{$command}->{flag}} split('', $::users->{person}->{$acct}->{flags}))) { #make sure the requester has the needed flags
- $fail = 1;
- }
- }
if ($cmd=~/$command/) {
my $where = $event->{to}[0];
if (index($where, '#') == -1) {
$where = 'PM';
}
ASM::Util->dprint("$event->{from} told me in $where: $cmd", "commander");
+
if (!ASM::Util->notRestricted($nick, "nocommands")) {
$fail = 1;
}
- if ($fail == 1) {
- $conn->privmsg($nick, "You don't have permission to use that command, or you're not signed into nickserv.");
- } else {
- &{$self->{cmdtbl}->{$command}->{cmd}}($conn, $event);
+
+ my $check_and_run_command = sub {
+ # If the command is restricted,
+ if ( my $flag = $self->{cmdtbl}->{$command}->{flag} ) {
+ # require an account
+ if (!defined($acct)) {
+ $fail = 1;
+ }
+ else {
+ # and check for the flag
+ my $user = $::db->resultset('User')->by_name($acct);
+ if (!defined $user || !$user->$flag) {
+ $fail = 1;
+ }
+ }
+ }
+
+ if ($fail == 1) {
+ $conn->privmsg($nick, "You don't have permission to use that command, or you're not signed into nickserv.");
+ } else {
+ &{$self->{cmdtbl}->{$command}->{cmd}}($conn, $event);
+ }
+ };
+
+ # Do we need to wrap the entire command - including the permission check - in a transaction?
+ # Be careful; due to re-establishing a DB connection, this requires the command's code to
+ # be idempotent. See the DBIx::Class::Storage documentation on the txn_do method for details.
+ if ($self->{cmdtbl}{$command}{txn}) {
+ $::db->txn_do($check_and_run_command);
}
+ else {
+ $check_and_run_command->();
+ }
+
last;
}
}
@@ -234,30 +265,12 @@ sub cmd_addwebuser {
$conn->privmsg($event->replyto, "This command must be used in PM. Try again WITH A DIFFERENT PASSWORD!");
return;
}
- use Apache::Htpasswd; use Apache::Htgroup;
- my $o_Htpasswd = new Apache::Htpasswd({passwdFile => $::settings->{web}->{userfile}, UseMD5 => 1});
- my $o_Htgroup = new Apache::Htgroup($::settings->{web}->{groupfile});
- my $user = lc $::sn{lc $event->{nick}}->{account};
- $o_Htpasswd->htDelete($user);
- $o_Htpasswd->htpasswd($user, $pass);
- $o_Htpasswd->writeInfo($user, strftime("%F %T", gmtime));
- $o_Htgroup->adduser($user, 'actionlogs');
- $o_Htgroup->save();
- $conn->privmsg($event->replyto, "Added $user to the list of authorized web users.")
-}
-
-sub cmd_delwebuser {
- my ($conn, $event) = @_;
+ my $user = $::db->resultset('User')->by_name(lc $::sn{lc $event->{nick}}->{account});
+ $user->passphrase($pass);
+ $user->update;
- my $user = lc $+{user};
- use Apache::Htpasswd;
- use Apache::Htgroup;
- my $o_Htpasswd = new Apache::Htpasswd({passwdFile => $::settings->{web}->{userfile}, UseMD5 => 1});
- my $o_Htgroup = new Apache::Htgroup($::settings->{web}->{groupfile});
- $o_Htpasswd->htDelete($user);
- $o_Htgroup->deleteuser($user, 'actionlogs');
- $o_Htgroup->save();
- $conn->privmsg($event->replyto, "Removed $user from the list of authorized web users.")
+ my $name = $user->name;
+ $conn->privmsg($event->replyto, "Added $name to the list of authorized web users.")
}
sub cmd_teredo {
@@ -594,40 +607,89 @@ sub cmd_investigate2 {
$conn->privmsg($event->nick, "Only 10 results are shown at a time. For more, do ;investigate2 $nick " . ($skip+1) . '.');
}
-sub cmd_user_add {
+sub get_user_flagstring {
+ my ($user) = @_;
+
+ my $string = '';
+
+ for my $letter (sort keys %letter_to_flag) {
+ my $flag = $letter_to_flag{$letter};
+
+ $string .= $letter if $user->$flag;
+ }
+
+ return $string;
+}
+
+sub set_user_flagstring {
+ my ($user, $string) = @_;
+
+ while (my ($letter, $flag) = each %letter_to_flag) {
+ if (index($string, $letter) != -1) {
+ $user->$flag(1);
+ }
+ else {
+ $user->$flag(0);
+ }
+ }
+}
+
+sub is_flagstring_superset {
+ my ($super, $sub) = @_;
+ for my $letter (split //, $sub) {
+ return 0 if index($super, $letter) == -1;
+ }
+ return 1;
+}
+
+sub cmd_user_set_flags {
my ($conn, $event) = @_;
my $nick = lc $+{account};
my $account;
my $flags = $+{flags};
- my %hasflagshash = ();
- foreach my $item (split(//, $::users->{person}->{lc $::sn{lc $event->{nick}}->{account}}->{flags})) {
- $hasflagshash{$item} = 1;
+
+ # we need to be idempotent if interrupted halfway.
+ # TODO: this is rather ugly / error-prone.
+ state $sent_message = 0;
+
+ if ( (defined($::sn{$nick}->{account})) && ( ($account = lc $::sn{$nick}->{account}) ne $nick ) ) {
+ $conn->privmsg($event->replyto, "I'm assuming you mean " . $nick . "'s nickserv account, " . $account . '.')
+ if !($sent_message++);
+ $nick = $account;
}
- foreach my $flag (split(//, $flags)) {
- if (!defined($hasflagshash{$flag})) {
- $conn->privmsg($event->replyto, "You can't give a flag you don't already have.");
- return;
- }
+
+ my $giver = $::db->resultset('User')->by_name( lc $::sn{lc $event->{nick}}{account} );
+
+ my $own_flags = get_user_flagstring($giver);
+
+ if (!is_flagstring_superset($own_flags, $flags)) {
+ $conn->privmsg($event->replyto, "You can't give a flag you don't already have.");
+ $sent_message = 0;
+ return;
}
if ($flags =~ /d/) {
- $conn->privmsg($event->replyto, "The d flag may not be assigned over IRC. Edit the configuration manually.");
+ $conn->privmsg($event->replyto, "The d flag may not be assigned over IRC. Edit the database manually.");
+ $sent_message = 0;
return;
}
- if ( (defined($::sn{$nick}->{account})) && ( ($account = lc $::sn{$nick}->{account}) ne $nick ) ) {
- $conn->privmsg($event->replyto, "I'm assuming you mean " . $nick . "'s nickserv account, " . $account . '.');
- $nick = $account;
- }
- if (defined($::users->{person}->{$nick})) {
- $conn->privmsg($event->replyto, "The user $nick already exists. Use ;user flags $nick $flags to set their flags");
+
+ my $target = $::db->resultset('User')->by_name_or_new( $nick );
+
+ if ($target->flag_debug) {
+ $conn->privmsg($event->replyto, "Users with the 'd' flag are untouchable. Edit the database manually.");
+ $sent_message = 0;
return;
}
- $::users->{person}->{$nick} = { 'flags' => $flags };
- ASM::Config->writeUsers();
+
+ set_user_flagstring($target, $flags);
+ $target->update_or_insert;
+
+ $sent_message = 0;
$conn->privmsg($event->replyto, "Flags for NickServ account $nick set to $flags");
}
-sub cmd_user_flags {
+sub cmd_user_get_flags {
my ($conn, $event) = @_;
my $nick = lc $+{account};
@@ -637,72 +699,32 @@ sub cmd_user_flags {
$nick = $account;
}
my $sayNick = substr($nick, 0, 1) . "\x02\x02" . substr($nick, 1);
- if (defined($::users->{person}->{$nick}->{flags})) {
- $conn->privmsg($event->replyto, "Flags for $sayNick: $::users->{person}->{$nick}->{flags}");
- } else {
- $conn->privmsg($event->replyto, "$sayNick has no flags");
- }
-}
-sub cmd_user_flags2 {
- my ($conn, $event) = @_;
+ my $user = $::db->resultset('User')->by_name($nick);
- my $nick = lc $+{account};
- my $flags = $+{flags};
- my $account;
- my %hasflagshash = ();
- foreach my $item (split(//, $::users->{person}->{lc $::sn{lc $event->{nick}}->{account}}->{flags})) {
- $hasflagshash{$item} = 1;
+ if (defined $user and length( my $flags = get_user_flagstring($user) )) {
+ $conn->privmsg($event->replyto, "Flags for $sayNick: $flags");
}
- foreach my $flag (split(//, $flags)) {
- if (!defined($hasflagshash{$flag})) {
- $conn->privmsg($event->replyto, "You can't give a flag you don't already have.");
- return;
- }
- }
- if ($flags =~ /d/) {
- $conn->privmsg($event->replyto, "The d flag may not be assigned over IRC. Edit the configuration manually.");
- return;
- }
- if ( (defined($::sn{$nick}->{account})) && ( ($account = lc $::sn{$nick}->{account}) ne $nick ) ) {
- $conn->privmsg($event->replyto, "I'm assuming you mean " . $nick . "'s nickserv account, " . $account . '.');
- $nick = $account;
- }
- if (defined($::users->{person}->{$nick}) &&
- defined($::users->{person}->{$nick}->{flags}) &&
- ($::users->{person}->{$nick}->{flags} =~ /d/)) {
- return $conn->privmsg($event->replyto, "Users with the 'd' flag are untouchable. Edit the config file manually.");
- }
- if ($flags !~ /s/) {
- use Apache::Htpasswd; use Apache::Htgroup;
- my $o_Htpasswd = new Apache::Htpasswd({passwdFile => $::settings->{web}->{userfile}, UseMD5 => 1});
- my $o_Htgroup = new Apache::Htgroup($::settings->{web}->{groupfile});
- $o_Htpasswd->htDelete($nick);
- $o_Htgroup->deleteuser($nick, 'actionlogs');
- $o_Htgroup->save();
+ else {
+ $conn->privmsg($event->replyto, "$sayNick has no flags");
}
- $::users->{person}->{$nick}->{flags} = $flags;
- ASM::Config->writeUsers();
- $conn->privmsg($event->replyto, "Flags for $nick set to $flags");
}
sub cmd_user_del {
my ($conn, $event) = @_;
my $nick = lc $+{account};
- if (defined($::users->{person}->{$nick}) &&
- defined($::users->{person}->{$nick}->{flags}) &&
- ($::users->{person}->{$nick}->{flags} =~ /d/)) {
- return $conn->privmsg($event->replyto, "Users with the 'd' flag are untouchable. Edit the config file manually.");
+
+ my $target = $::db->resultset('User')->by_name($nick);
+ if (!defined $target) {
+ $conn->privmsg($event->replyto, "I know no user by that name. Make sure you specified the account name.");
+ return;
+ }
+ if ($target->flag_debug) {
+ $conn->privmsg($event->replyto, "Users with the 'd' flag are untouchable. Edit the database manually.");
+ return;
}
- delete($::users->{person}->{$nick});
- ASM::Config->writeUsers();
- use Apache::Htpasswd; use Apache::Htgroup;
- my $o_Htpasswd = new Apache::Htpasswd({passwdFile => $::settings->{web}->{userfile}, UseMD5 => 1});
- my $o_Htgroup = new Apache::Htgroup($::settings->{web}->{groupfile});
- $o_Htpasswd->htDelete($nick);
- $o_Htgroup->deleteuser($nick, 'actionlogs');
- $o_Htgroup->save();
+ $target->delete;
$conn->privmsg($event->replyto, "Removed $nick from authorized users." .
" MAKE SURE YOU PROVIDED a nickserv account to this command, rather than an altnick of the accountholder");
}
diff --git a/lib/ASM/Config.pm b/lib/ASM/Config.pm
index ff6f745..8e336a3 100644
--- a/lib/ASM/Config.pm
+++ b/lib/ASM/Config.pm
@@ -19,7 +19,6 @@ sub deserialize {
sub readConfig {
$::settings = deserialize(io->file("$::cset/settings.json")->all);
$::channels = deserialize(io->file("$::cset/channels.json")->all);
- $::users = deserialize(io->file("$::cset/users.json")->all);
$::mysql = deserialize(io->file("$::cset/mysql.json")->all);
$::dnsbl = deserialize(io->file("$::cset/dnsbl.json")->all);
$::rules = deserialize(io->file("$::cset/rules.json")->all);
@@ -30,7 +29,6 @@ sub readConfig {
sub writeConfig {
writeMysql();
writeChannels();
- writeUsers();
writeSettings();
writeRestrictions();
writeBlacklist();
@@ -58,11 +56,6 @@ sub writeChannels {
serialize($::channels) > io("$::cset/channels.json");
}
-sub writeUsers {
- $::settingschanged=1;
- serialize($::users) > io("$::cset/users.json");
-}
-
sub writeSettings {
$::settingschanged=1;
serialize($::settings) > io("$::cset/settings.json");
diff --git a/lib/ASM/DB.pm b/lib/ASM/DB.pm
index cc9906c..5e1d44d 100644
--- a/lib/ASM/DB.pm
+++ b/lib/ASM/DB.pm
@@ -1,5 +1,5 @@
use utf8;
-package ASM::DB 3;
+package ASM::DB 4;
use strict;
use warnings;
diff --git a/lib/ASM/DB/Result/User.pm b/lib/ASM/DB/Result/User.pm
index 5e5d17c..675ccba 100644
--- a/lib/ASM/DB/Result/User.pm
+++ b/lib/ASM/DB/Result/User.pm
@@ -6,6 +6,8 @@ use warnings;
use parent 'DBIx::Class::Core';
+use Authen::Passphrase::RejectAll;
+
__PACKAGE__->load_components('InflateColumn::DateTime', 'PassphraseColumn');
__PACKAGE__->table('users');
__PACKAGE__->add_columns(
@@ -30,9 +32,41 @@ __PACKAGE__->add_columns(
passphrase_check_method => 'check_password',
is_nullable => 0,
},
+ flag_secret => {
+ data_type => 'boolean',
+ is_nullable => 0,
+ default_value => 0,
+ },
+ flag_hilights => {
+ data_type => 'boolean',
+ is_nullable => 0,
+ default_value => 0,
+ },
+ flag_admin => {
+ data_type => 'boolean',
+ is_nullable => 0,
+ default_value => 0,
+ },
+ flag_plugin => {
+ data_type => 'boolean',
+ is_nullable => 0,
+ default_value => 0,
+ },
+ flag_debug => {
+ data_type => 'boolean',
+ is_nullable => 0,
+ default_value => 0,
+ },
);
__PACKAGE__->set_primary_key('id');
__PACKAGE__->add_unique_constraint(uniq_user_name => ['name']);
+sub new {
+ my $self = shift;
+ $_[0]{passphrase} //= Authen::Passphrase::RejectAll->new;
+
+ $self->SUPER::new(@_);
+}
+
1;
diff --git a/lib/ASM/DB/ResultSet/User.pm b/lib/ASM/DB/ResultSet/User.pm
new file mode 100644
index 0000000..68c0871
--- /dev/null
+++ b/lib/ASM/DB/ResultSet/User.pm
@@ -0,0 +1,23 @@
+use utf8;
+
+package ASM::DB::ResultSet::User;
+
+use strict;
+use warnings;
+
+use parent 'DBIx::Class::ResultSet';
+use namespace::autoclean;
+
+sub by_name {
+ my ( $self, $name ) = @_;
+
+ return $self->find( { name => $name }, { key => 'uniq_user_name' } );
+}
+
+sub by_name_or_new {
+ my ( $self, $name ) = @_;
+
+ return $self->find_or_new( { name => $name }, { key => 'uniq_user_name' } );
+}
+
+1;
diff --git a/sql/_common/upgrade/3-4/002-convert_users.pl b/sql/_common/upgrade/3-4/002-convert_users.pl
new file mode 100644
index 0000000..9631345
--- /dev/null
+++ b/sql/_common/upgrade/3-4/002-convert_users.pl
@@ -0,0 +1,31 @@
+#!/usr/bin/env perl
+use v5.20;
+use warnings;
+
+use DBIx::Class::DeploymentHandler::DeployMethod::SQL::Translator::ScriptHelpers 'schema_from_schema_loader';
+use Authen::Passphrase::RejectAll;
+
+schema_from_schema_loader({ naming => 'v4' }, sub {
+ my ($schema, $versions) = @_;
+
+ while (my ($name, $user) = each %{ $::users->{person} }) {
+ my %flags;
+ for my $flag (split(//, $user->{flags})) {
+ $flags{$flag} = 1;
+ }
+ $schema->resultset('Users')->create({
+ name => $name,
+ # We don't have the manually adjusted schema, so we need to
+ # pass the raw value for the passphrase column
+ passphrase => Authen::Passphrase::RejectAll->new->as_rfc2307,
+ flag_secret => $flags{s} // 0,
+ flag_admin => $flags{a} // 0,
+ flag_hilights => $flags{h} // 0,
+ flag_debug => $flags{d} // 0,
+ flag_plugin => $flags{p} // 0,
+ });
+ }
+
+ say "NOTE: The data from users.json has been moved to the database.\n"
+ . "You may remove users.json now, although keeping a backup is strongly recommended.";
+ })
diff --git a/sql/_source/deploy/4/001-auto-__VERSION.yml b/sql/_source/deploy/4/001-auto-__VERSION.yml
new file mode 100644
index 0000000..907f443
--- /dev/null
+++ b/sql/_source/deploy/4/001-auto-__VERSION.yml
@@ -0,0 +1,91 @@
+---
+schema:
+ procedures: {}
+ tables:
+ dbix_class_deploymenthandler_versions:
+ constraints:
+ - deferrable: 1
+ expression: ''
+ fields:
+ - id
+ match_type: ''
+ name: ''
+ on_delete: ''
+ on_update: ''
+ options: []
+ reference_fields: []
+ reference_table: ''
+ type: PRIMARY KEY
+ - deferrable: 1
+ expression: ''
+ fields:
+ - version
+ match_type: ''
+ name: dbix_class_deploymenthandler_versions_version
+ on_delete: ''
+ on_update: ''
+ options: []
+ reference_fields: []
+ reference_table: ''
+ type: UNIQUE
+ fields:
+ ddl:
+ data_type: text
+ default_value: ~
+ is_nullable: 1
+ is_primary_key: 0
+ is_unique: 0
+ name: ddl
+ order: 3
+ size:
+ - 0
+ id:
+ data_type: int
+ default_value: ~
+ is_auto_increment: 1
+ is_nullable: 0
+ is_primary_key: 1
+ is_unique: 0
+ name: id
+ order: 1
+ size:
+ - 0
+ upgrade_sql:
+ data_type: text
+ default_value: ~
+ is_nullable: 1
+ is_primary_key: 0
+ is_unique: 0
+ name: upgrade_sql
+ order: 4
+ size:
+ - 0
+ version:
+ data_type: varchar
+ default_value: ~
+ is_nullable: 0
+ is_primary_key: 0
+ is_unique: 1
+ name: version
+ order: 2
+ size:
+ - 50
+ indices: []
+ name: dbix_class_deploymenthandler_versions
+ options: []
+ order: 1
+ triggers: {}
+ views: {}
+translator:
+ add_drop_table: 0
+ filename: ~
+ no_comments: 0
+ parser_args:
+ sources:
+ - __VERSION
+ parser_type: SQL::Translator::Parser::DBIx::Class
+ producer_args: {}
+ producer_type: SQL::Translator::Producer::YAML
+ show_warnings: 0
+ trace: 0
+ version: 0.11021
diff --git a/sql/_source/deploy/4/001-auto.yml b/sql/_source/deploy/4/001-auto.yml
new file mode 100644
index 0000000..8ae9ddc
--- /dev/null
+++ b/sql/_source/deploy/4/001-auto.yml
@@ -0,0 +1,437 @@
+---
+schema:
+ procedures: {}
+ tables:
+ actionlog:
+ constraints:
+ - deferrable: 1
+ expression: ''
+ fields:
+ - index
+ match_type: ''
+ name: ''
+ on_delete: ''
+ on_update: ''
+ options: []
+ reference_fields: []
+ reference_table: ''
+ type: PRIMARY KEY
+ fields:
+ account:
+ data_type: varchar
+ default_value: ~
+ is_nullable: 1
+ is_primary_key: 0
+ is_unique: 0
+ name: account
+ order: 11
+ size:
+ - 17
+ action:
+ data_type: varchar
+ default_value: ~
+ is_nullable: 0
+ is_primary_key: 0
+ is_unique: 0
+ name: action
+ order: 3
+ size:
+ - 20
+ byaccount:
+ data_type: varchar
+ default_value: ~
+ is_nullable: 1
+ is_primary_key: 0
+ is_unique: 0
+ name: byaccount
+ order: 16
+ size:
+ - 17
+ bygecos:
+ data_type: varchar
+ default_value: ~
+ is_nullable: 1
+ is_primary_key: 0
+ is_unique: 0
+ name: bygecos
+ order: 15
+ size:
+ - 512
+ byhost:
+ data_type: varchar
+ default_value: ~
+ is_nullable: 1
+ is_primary_key: 0
+ is_unique: 0
+ name: byhost
+ order: 14
+ size:
+ - 64
+ bynick:
+ data_type: varchar
+ default_value: ~
+ is_nullable: 1
+ is_primary_key: 0
+ is_unique: 0
+ name: bynick
+ order: 12
+ size:
+ - 17
+ byuser:
+ data_type: varchar
+ default_value: ~
+ is_nullable: 1
+ is_primary_key: 0
+ is_unique: 0
+ name: byuser
+ order: 13
+ size:
+ - 11
+ channel:
+ data_type: varchar
+ default_value: ~
+ is_nullable: 1
+ is_primary_key: 0
+ is_unique: 0
+ name: channel
+ order: 5
+ size:
+ - 51
+ gecos:
+ data_type: varchar
+ default_value: ~
+ is_nullable: 1
+ is_primary_key: 0
+ is_unique: 0
+ name: gecos
+ order: 10
+ size:
+ - 512
+ host:
+ data_type: varchar
+ default_value: ~
+ is_nullable: 1
+ is_primary_key: 0
+ is_unique: 0
+ name: host
+ order: 8
+ size:
+ - 64
+ index:
+ data_type: bigint
+ default_value: ~
+ is_auto_increment: 1
+ is_nullable: 0
+ is_primary_key: 1
+ is_unique: 0
+ name: index
+ order: 1
+ size:
+ - 0
+ ip:
+ data_type: integer
+ default_value: ~
+ extra:
+ unsigned: 1
+ is_nullable: 1
+ is_primary_key: 0
+ is_unique: 0
+ name: ip
+ order: 9
+ size:
+ - 0
+ nick:
+ data_type: varchar
+ default_value: ~
+ is_nullable: 0
+ is_primary_key: 0
+ is_unique: 0
+ name: nick
+ order: 6
+ size:
+ - 17
+ reason:
+ data_type: varchar
+ default_value: ~
+ is_nullable: 1
+ is_primary_key: 0
+ is_unique: 0
+ name: reason
+ order: 4
+ size:
+ - 512
+ time:
+ data_type: timestamp
+ default_value: !!perl/ref
+ =: current_timestamp
+ is_nullable: 0
+ is_primary_key: 0
+ is_unique: 0
+ name: time
+ order: 2
+ size:
+ - 0
+ user:
+ data_type: varchar
+ default_value: ~
+ is_nullable: 1
+ is_primary_key: 0
+ is_unique: 0
+ name: user
+ order: 7
+ size:
+ - 11
+ indices: []
+ name: actionlog
+ options: []
+ order: 1
+ alertlog:
+ constraints:
+ - deferrable: 1
+ expression: ''
+ fields:
+ - sqlid
+ match_type: ''
+ name: ''
+ on_delete: ''
+ on_update: ''
+ options: []
+ reference_fields: []
+ reference_table: ''
+ type: PRIMARY KEY
+ fields:
+ channel:
+ data_type: text
+ default_value: ~
+ is_nullable: 0
+ is_primary_key: 0
+ is_unique: 0
+ name: channel
+ order: 2
+ size:
+ - 0
+ gecos:
+ data_type: text
+ default_value: ~
+ is_nullable: 0
+ is_primary_key: 0
+ is_unique: 0
+ name: gecos
+ order: 6
+ size:
+ - 0
+ host:
+ data_type: text
+ default_value: ~
+ is_nullable: 0
+ is_primary_key: 0
+ is_unique: 0
+ name: host
+ order: 5
+ size:
+ - 0
+ id:
+ data_type: tinytext
+ default_value: ~
+ is_nullable: 0
+ is_primary_key: 0
+ is_unique: 0
+ name: id
+ order: 8
+ size:
+ - 0
+ level:
+ data_type: tinytext
+ default_value: ~
+ is_nullable: 0
+ is_primary_key: 0
+ is_unique: 0
+ name: level
+ order: 7
+ size:
+ - 0
+ nick:
+ data_type: text
+ default_value: ~
+ is_nullable: 0
+ is_primary_key: 0
+ is_unique: 0
+ name: nick
+ order: 3
+ size:
+ - 0
+ reason:
+ data_type: text
+ default_value: ~
+ is_nullable: 0
+ is_primary_key: 0
+ is_unique: 0
+ name: reason
+ order: 9
+ size:
+ - 0
+ sqlid:
+ data_type: bigint
+ default_value: ~
+ is_auto_increment: 1
+ is_nullable: 0
+ is_primary_key: 1
+ is_unique: 0
+ name: sqlid
+ order: 10
+ size:
+ - 0
+ time:
+ data_type: timestamp
+ default_value: !!perl/ref
+ =: current_timestamp
+ is_nullable: 0
+ is_primary_key: 0
+ is_unique: 0
+ name: time
+ order: 1
+ size:
+ - 0
+ user:
+ data_type: text
+ default_value: ~
+ is_nullable: 0
+ is_primary_key: 0
+ is_unique: 0
+ name: user
+ order: 4
+ size:
+ - 0
+ indices: []
+ name: alertlog
+ options: []
+ order: 2
+ users:
+ constraints:
+ - deferrable: 1
+ expression: ''
+ fields:
+ - id
+ match_type: ''
+ name: ''
+ on_delete: ''
+ on_update: ''
+ options: []
+ reference_fields: []
+ reference_table: ''
+ type: PRIMARY KEY
+ - deferrable: 1
+ expression: ''
+ fields:
+ - name
+ match_type: ''
+ name: uniq_user_name
+ on_delete: ''
+ on_update: ''
+ options: []
+ reference_fields: []
+ reference_table: ''
+ type: UNIQUE
+ fields:
+ flag_admin:
+ data_type: boolean
+ default_value: 0
+ is_nullable: 0
+ is_primary_key: 0
+ is_unique: 0
+ name: flag_admin
+ order: 6
+ size:
+ - 0
+ flag_debug:
+ data_type: boolean
+ default_value: 0
+ is_nullable: 0
+ is_primary_key: 0
+ is_unique: 0
+ name: flag_debug
+ order: 8
+ size:
+ - 0
+ flag_hilights:
+ data_type: boolean
+ default_value: 0
+ is_nullable: 0
+ is_primary_key: 0
+ is_unique: 0
+ name: flag_hilights
+ order: 5
+ size:
+ - 0
+ flag_plugin:
+ data_type: boolean
+ default_value: 0
+ is_nullable: 0
+ is_primary_key: 0
+ is_unique: 0
+ name: flag_plugin
+ order: 7
+ size:
+ - 0
+ flag_secret:
+ data_type: boolean
+ default_value: 0
+ is_nullable: 0
+ is_primary_key: 0
+ is_unique: 0
+ name: flag_secret
+ order: 4
+ size:
+ - 0
+ id:
+ data_type: bigint
+ default_value: ~
+ is_auto_increment: 1
+ is_nullable: 0
+ is_primary_key: 1
+ is_unique: 0
+ name: id
+ order: 1
+ size:
+ - 0
+ name:
+ data_type: varchar
+ default_value: ~
+ is_nullable: 0
+ is_primary_key: 0
+ is_unique: 1
+ name: name
+ order: 2
+ size:
+ - 20
+ passphrase:
+ data_type: text
+ default_value: ~
+ is_nullable: 0
+ is_primary_key: 0
+ is_unique: 0
+ name: passphrase
+ order: 3
+ size:
+ - 0
+ indices: []
+ name: users
+ options: []
+ order: 3
+ triggers: {}
+ views: {}
+translator:
+ add_drop_table: 0
+ filename: ~
+ no_comments: 0
+ parser_args:
+ sources:
+ - Actionlog
+ - Alertlog
+ - User
+ parser_type: SQL::Translator::Parser::DBIx::Class
+ producer_args: {}
+ producer_type: SQL::Translator::Producer::YAML
+ show_warnings: 0
+ trace: 0
+ version: 0.11021