From: Jess Robinson Date: Thu, 10 May 2012 13:31:44 +0000 (+0000) Subject: Complete initial admin_users script, including deactivate, reactivate, really delete... X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=570cf8ba28203b3e022e019fdad0b8d95872bb9d;p=scpubgit%2Fstemmatology.git Complete initial admin_users script, including deactivate, reactivate, really delete. Also tests! --- diff --git a/lib/Text/Tradition/User.pm b/lib/Text/Tradition/User.pm index 9071062..6c21a7d 100644 --- a/lib/Text/Tradition/User.pm +++ b/lib/Text/Tradition/User.pm @@ -8,10 +8,11 @@ with qw(KiokuX::User); ## 'id' provided by KiokuX::User stores our username has 'password' => (is => 'rw', required => 1); +## Change this default active value if you want/need to have an admin confirm a user after they self-create. has 'active' => (is => 'rw', default => sub { 1; }); # 'traits' => ['Array'] ? # https://metacpan.org/module/Moose::Meta::Attribute::Native::Trait::Array -has 'traditions' => (is => 'rw', isa => 'ArrayRef[Text::Tradition]', required => 0); +has 'traditions' => (is => 'rw', isa => 'ArrayRef[Text::Tradition]', default => sub { [] }, required => 0); # after add_tradition => sub { # $tradition->set_user($self) diff --git a/lib/Text/Tradition/UserStore.pm b/lib/Text/Tradition/UserStore.pm index dbcba48..5f1ce41 100644 --- a/lib/Text/Tradition/UserStore.pm +++ b/lib/Text/Tradition/UserStore.pm @@ -9,6 +9,7 @@ use KiokuX::User::Util qw(crypt_password); extends 'KiokuX::Model'; use Text::Tradition::User; +# use Text::Tradition::Directory; has MIN_PASS_LEN => ( is => 'ro', isa => 'Num', default => sub { 8 } ); @@ -18,6 +19,13 @@ has MIN_PASS_LEN => ( is => 'ro', isa => 'Num', default => sub { 8 } ); # handles => [] # ); +## TODO: Some of these methods should probably optionally take $user objects +## instead of hashrefs. + +## It also occurs to me that all these methods don't need to be named +## XX_user, but leaving that way for now incase we merge this code +## into ::Directory for one-store. + ## To die or not to die, on error, this is the question. sub add_user { my ($self, $userinfo) = @_; @@ -41,6 +49,7 @@ sub find_user { my ($self, $userinfo) = @_; my $username = $userinfo->{username}; + my $scope = $self->new_scope; return $self->lookup(Text::Tradition::User->id_for_user($username)); } @@ -63,9 +72,67 @@ sub modify_user { return $user; } -sub delete_user { +sub deactivate_user { + my ($self, $userinfo) = @_; + my $username = $userinfo->{username}; + + return if !$username; + + my $user = $self->find_user({ username => $username }); + return if !$user; + + $user->active(0); + foreach my $tradition (@{ $user->traditions }) { + ## Not implemented yet + # $tradition->public(0); + } + my $scope = $self->new_scope; + + ## Should we be using Text::Tradition::Directory also? + $self->update(@{ $user->traditions }); + + $self->update($user); + + return $user; } +sub reactivate_user { + my ($self, $userinfo) = @_; + my $username = $userinfo->{username}; + + return if !$username; + + my $user = $self->find_user({ username => $username }); + return if !$user; + + return $user if $user->active; + + $user->active(1); + my $scope = $self->new_scope; + $self->update($user); + + return $user; +} + +sub delete_user { + my ($self, $userinfo) = @_; + my $username = $userinfo->{username}; + + return if !$username; + + my $user = $self->find_user({ username => $username }); + return if !$user; + + my $scope = $self->new_scope; + + ## Should we be using Text::Tradition::Directory for this bit? + $self->delete( @{ $user->traditions }); + + ## Poof, gone. + $self->delete($user); + + return 1; +} sub validate_password { my ($self, $password) = @_; diff --git a/script/admin_users.pl b/script/admin_users.pl new file mode 100644 index 0000000..35aaebe --- /dev/null +++ b/script/admin_users.pl @@ -0,0 +1,135 @@ +#!/usr/bin/env perl + +use strict; +use warnings; + +use v5.10.0; + +use Getopt::Long; +## using prompt(): +use ExtUtils::MakeMaker(); +use Text::Tradition::UserStore; + +use lib 'lib'; + +my ($dsn, $command) = ('dbi:SQLite:dbname=db/traditions.db', 'add', undef); +my ($username, $password); + +GetOptions( + 'c|command:s' => \$command, + 'dsn:s' => \$dsn, + 'u|username=s' => \$username, + 'p|password:s' => \$password, + ) or usage(); + +if(!$command || !($command ~~ [qw/add modify delete deactivate reactivate/])) { + print "No command supplied, chickening out ... \n\n"; + usage(); +} + +if(!$username) { + print "No username supplied, confused ... \n\n"; + usage(); +} + +my $userstore = Text::Tradition::UserStore->new( dsn => $dsn); + +given ($command) { + when ('add') { + if(!$password || !$userstore->validate_password($password)) { + print "Can't add a new user without a valid password\n\n"; + usage(); + } + my $user = $userstore->add_user({ username => $username, + password => $password }); + if(!$user) { + print "Failed to add user! (you should see errors above this..)\n"; + } else { + print "OK.\n"; + } + } + + when ('modify') { + if(!$password || !$userstore->validate_password($password)) { + print "Can't modify a user without a valid password\n\n"; + usage(); + } + my $user = $userstore->modify_user({ username => $username, + password => $password }); + if(!$user) { + print "Failed to modify user! (you should see errors above this..)\n"; + } else { + print "OK.\n"; + } + } + + when ('deactivate') { + my $user = $userstore->deactivate_user({ username => $username}); + if(!$user) { + print "Failed to deactivate user! (you should see errors above this..)\n"; + } else { + print "OK.\n"; + } + } + + when ('reactivate') { + my $user = $userstore->reactivate_user({ username => $username}); + if(!$user) { + print "Failed to reactivate user! (you should see errors above this..)\n"; + } else { + print "OK.\n"; + } + } + + when ('delete') { + my $yesno = ExtUtils::MakeMaker::prompt("Permanently delete $username? (y/N)", "n"); + if($yesno !~ /^y$/i) { + print "Not deleting $username\n"; + break; + } + my $user = $userstore->delete_user({ username => $username}); + if(!$user) { + print "Failed to delete user! (you should see errors above this..)\n"; + } else { + print "OK.\n"; + } + } +} + +sub usage { + print "User Admin tool, to add/modify/deactivate/reactivate/delete users\n"; + print "===========================================\n"; + print "Usage: $0 -c add -u jimbob -p hispassword\n"; + print "Usage: $0 -c modify -u jimbob -p hisnewpassword\n"; + print "Usage: $0 -c deactivate -u jimbob\n"; +} + +=head1 NAME + +admin_users.pl - add / modify / etc users + +=head1 SYNOPSIS + + admin_user.pl -c add -u jimbob -p "jimspassword" + + admin_user.pl -c modify -u jimbob -p "jimsnewpassword" + + admin_user.pl -c delete -u jimbob + +=head1 OPTIONS + +=over + +=item -c | --command + +The action to take, can be one of: add, modify, deactivate, reactivate, delete. + +=item -u | --username + +The username of the new user or user to change. + +=item -p | --password + +The new password or password to change. + +=back diff --git a/t/text_tradition_user.t b/t/text_tradition_user.t index 4a01463..38773a0 100644 --- a/t/text_tradition_user.t +++ b/t/text_tradition_user.t @@ -25,6 +25,7 @@ ok(!$user_store->validate_password($shortpass), '"bloggs" is too short for a pas my $new_user = $user_store->add_user({ username => 'fred', password => 'bloggspass'}); isa_ok($new_user, 'Text::Tradition::User'); +is($new_user->active, 1, 'New user created and active'); ## find user my $find_user = $user_store->find_user({ username => 'fred'}); @@ -38,4 +39,41 @@ isa_ok($changed_user, 'Text::Tradition::User'); my $changed = $user_store->find_user({ username => 'fred'}); ok($changed->check_password('passbloggs'), 'Modified & retrieved with correct new password'); +{ +## deactivate user +## Sets all traditions to non-public, deactivates + my $user = $user_store->add_user({ username => 'testactive', + password => 'imanactiveuser' }); + ok($user->active, 'Deactivate test user starts active'); + + my $d_user = $user_store->deactivate_user({ username => 'testactive' }); + is($d_user->active, 0, 'Deactivated user'); + +## TODO - add test where user has traditions to start with +} + +{ +## reactivate user +## reactivates user, does not mess with their traditions (as we don't know which were public to start with) + + my $user = $user_store->add_user({ username => 'testinactive', + password => 'imaninactiveuser' }); + my $d_user = $user_store->deactivate_user({ username => 'testactive' }); + ok(!$d_user->active, 'Deactivate test user starts active'); + + my $a_user = $user_store->reactivate_user({ username => 'testinactive' }); + is($a_user->active, 1, 'Re-activated user'); +} + +{ +## delete user (admin only?) + my $user = $user_store->add_user({ username => 'testdelete', + password => 'imgoingtobedeleted' }); + + my $gone = $user_store->delete_user({ username => 'testdelete' }); + + my $d_user = $user_store->find_user({ username => 'testdelete' }); + + ok($gone && !$d_user, 'Deleted user completely from store'); +}