diff options
| author | 2017-05-08 02:06:38 +0000 | |
|---|---|---|
| committer | 2017-05-08 02:25:18 +0000 | |
| commit | 7342c7f0e19e15ab3c7ba2133a56393c15989f08 (patch) | |
| tree | 5e00a9c2a3164bd49d18ded9263f1ea7ae99d7f6 | |
| parent | 62d8fcde1c6dc5be2f535f6fcffa1afd838b62bd (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-- | .gitignore | 1 | ||||
| -rw-r--r-- | config-default/settings.json | 2 | ||||
| -rw-r--r-- | cpanfile | 3 | ||||
| -rw-r--r-- | cpanfile.snapshot | 13 | ||||
| -rw-r--r-- | lib/ASM/Commander.pm | 338 | ||||
| -rw-r--r-- | lib/ASM/Config.pm | 7 | ||||
| -rw-r--r-- | lib/ASM/DB.pm | 2 | ||||
| -rw-r--r-- | lib/ASM/DB/Result/User.pm | 34 | ||||
| -rw-r--r-- | lib/ASM/DB/ResultSet/User.pm | 23 | ||||
| -rw-r--r-- | sql/_common/upgrade/3-4/002-convert_users.pl | 31 | ||||
| -rw-r--r-- | sql/_source/deploy/4/001-auto-__VERSION.yml | 91 | ||||
| -rw-r--r-- | sql/_source/deploy/4/001-auto.yml | 437 |
12 files changed, 812 insertions, 170 deletions
@@ -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" } } @@ -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 |
