diff --git a/deps.nix b/deps.nix index ef9585b8..b6780a0e 100644 --- a/deps.nix +++ b/deps.nix @@ -5,6 +5,7 @@ with pkgs; [ perlPackages.CatalystAuthenticationStoreDBIxClass perlPackages.CatalystPluginAccessLog perlPackages.CatalystPluginAuthorizationRoles + perlPackages.CatalystPluginCaptcha perlPackages.CatalystPluginSessionStateCookie perlPackages.CatalystPluginSessionStoreFastMmap perlPackages.CatalystPluginStackTrace diff --git a/src/lib/Hydra.pm b/src/lib/Hydra.pm index 44c899b4..c4f69d22 100644 --- a/src/lib/Hydra.pm +++ b/src/lib/Hydra.pm @@ -13,7 +13,8 @@ use Catalyst qw/ConfigLoader Session Session::Store::FastMmap Session::State::Cookie - AccessLog/, + AccessLog + Captcha/, '-Log=warn,fatal,error'; our $VERSION = '0.01'; @@ -56,6 +57,24 @@ __PACKAGE__->config( format => '%h %l %u %t "%r" %s %b "%{Referer}i" "%{User-Agent}i" %[handle_time]', }, }, + 'Plugin::Captcha' => { + session_name => 'hydra-captcha', + new => { + width => 270, + height => 80, + ptsize => 20, + lines => 30, + thickness => 1, + rndmax => 5, + scramble => 1, + #send_ctobg => 1, + bgcolor => '#ffffff', + font => '/home/eelco/Dev/hydra/ttf/StayPuft.ttf', + }, + create => [ qw/ttf circle/ ], + particle => [ 3500 ], + out => { force => 'jpeg' } + }, ); __PACKAGE__->setup(); diff --git a/src/lib/Hydra/Controller/Admin.pm b/src/lib/Hydra/Controller/Admin.pm index 7e8e4245..a61719aa 100644 --- a/src/lib/Hydra/Controller/Admin.pm +++ b/src/lib/Hydra/Controller/Admin.pm @@ -87,6 +87,7 @@ sub create_user : Chained('admin') PathPart('create-user') Args(0) { $c->stash->{create} = 1; } + sub create_user_submit : Chained('admin') PathPart('create-user/submit') Args(0) { my ($self, $c) = @_; diff --git a/src/lib/Hydra/Controller/Root.pm b/src/lib/Hydra/Controller/Root.pm index 15dcab50..61f8b4f4 100644 --- a/src/lib/Hydra/Controller/Root.pm +++ b/src/lib/Hydra/Controller/Root.pm @@ -20,7 +20,9 @@ sub begin :Private { $c->stash->{nixVersion} = $ENV{"NIX_RELEASE"} || ""; $c->stash->{curTime} = time; $c->stash->{logo} = $ENV{"HYDRA_LOGO"} ? "/logo" : ""; - $c->stash->{tracker} = $ENV{"HYDRA_TRACKER"} ; + $c->stash->{tracker} = $ENV{"HYDRA_TRACKER"}; + $c->stash->{flashMsg} = $c->flash->{flashMsg}; + $c->stash->{successMsg} = $c->flash->{successMsg}; if (scalar(@args) == 0 || $args[0] ne "static") { $c->stash->{nrRunningBuilds} = $c->model('DB::Builds')->search({ finished => 0, busy => 1 }, {})->count(); @@ -37,46 +39,12 @@ sub index :Path :Args(0) { } -sub login :Local { - my ($self, $c) = @_; - - my $username = $c->request->params->{username} || ""; - my $password = $c->request->params->{password} || ""; - - if ($username eq "" && $password eq "" && !defined $c->flash->{referer}) { - my $baseurl = $c->uri_for('/'); - my $refurl = $c->request->referer; - $c->flash->{referer} = $refurl if $refurl =~ m/^($baseurl)/; - } - - if ($username && $password) { - if ($c->authenticate({username => $username, password => $password})) { - $c->response->redirect($c->flash->{referer} || $c->uri_for('/')); - $c->flash->{referer} = undef; - return; - } - $c->stash->{errorMsg} = "Bad username or password."; - } - - $c->keep_flash("referer"); - - $c->stash->{template} = 'login.tt'; -} - - -sub logout :Local { - my ($self, $c) = @_; - $c->logout; - $c->response->redirect($c->request->referer || $c->uri_for('/')); -} - - sub queue :Local { my ($self, $c) = @_; $c->stash->{template} = 'queue.tt'; $c->stash->{queue} = [$c->model('DB::Builds')->search( {finished => 0}, { join => ['project'], order_by => ["priority DESC", "timestamp"], columns => [@buildListColumns], '+select' => ['project.enabled'], '+as' => ['enabled'] })]; - $c->stash->{flashMsg} = $c->flash->{buildMsg}; + $c->stash->{flashMsg} //= $c->flash->{buildMsg}; } diff --git a/src/lib/Hydra/Controller/User.pm b/src/lib/Hydra/Controller/User.pm new file mode 100644 index 00000000..a3032a3d --- /dev/null +++ b/src/lib/Hydra/Controller/User.pm @@ -0,0 +1,112 @@ +package Hydra::Controller::User; + +use strict; +use warnings; +use base 'Catalyst::Controller'; +use Digest::SHA1 qw(sha1_hex); +use Hydra::Helper::Nix; +use Hydra::Helper::CatalystUtils; + + +__PACKAGE__->config->{namespace} = ''; + + +sub login :Local { + my ($self, $c) = @_; + + my $username = $c->request->params->{username} || ""; + my $password = $c->request->params->{password} || ""; + + if ($username eq "" && $password eq "" && !defined $c->flash->{referer}) { + my $baseurl = $c->uri_for('/'); + my $refurl = $c->request->referer; + $c->flash->{referer} = $refurl if $refurl =~ m/^($baseurl)/; + } + + if ($username && $password) { + if ($c->authenticate({username => $username, password => $password})) { + $c->response->redirect($c->flash->{referer} || $c->uri_for('/')); + $c->flash->{referer} = undef; + return; + } + $c->stash->{errorMsg} = "Bad username or password."; + } + + $c->keep_flash("referer"); + + $c->stash->{template} = 'login.tt'; +} + + +sub logout :Local { + my ($self, $c) = @_; + $c->logout; + $c->response->redirect($c->request->referer || $c->uri_for('/')); +} + + +sub captcha :Local Args(0) { + my ($self, $c) = @_; + $c->create_captcha(); +} + + +sub register :Local Args(0) { + my ($self, $c) = @_; + + $c->stash->{template} = 'user.tt'; + $c->stash->{create} = 1; + return if $c->request->method ne "POST"; + + my $userName = trim $c->req->params->{username}; + my $fullName = trim $c->req->params->{fullname}; + my $password = trim $c->req->params->{password}; + $c->stash->{username} = $userName; + $c->stash->{fullname} = $fullName; + + sub fail { + my ($c, $msg) = @_; + $c->stash->{errorMsg} = $msg; + } + + return fail($c, "You did not enter the correct digits from the security image.") + unless $c->validate_captcha($c->req->param('captcha')); + + return fail($c, "Your user name is invalid. It must start with a lower-case letter followed by lower-case letters, digits, dots or underscores.") + if $userName !~ /^$userNameRE$/; + + return fail($c, "Your user name is already taken.") + if $c->find_user({ username => $userName }); + + return fail($c, "Your must specify your full name.") if $fullName eq ""; + + return fail($c, "You must specify a password of at least 6 characters.") + if length($password) < 6; + + return fail($c, "The passwords you specified did not match.") + if $password ne trim $c->req->params->{password2}; + + txn_do($c->model('DB')->schema, sub { + my $user = $c->model('DB::Users')->create( + { username => $userName + , fullname => $fullName + , password => sha1_hex($password) + , emailaddress => "", + }); + }); + + $c->authenticate({username => $userName, password => $password}) + or error($c, "Unable to authenticate the new user!"); + + $c->flash->{successMsg} = "User $userName has been created."; + $c->response->redirect($c->flash->{referer} || $c->uri_for('/')); +} + + +sub preferences :Local Args(0) { + my ($self, $c) = @_; + error($c, "Not implemented."); +} + + +1; diff --git a/src/lib/Hydra/Helper/CatalystUtils.pm b/src/lib/Hydra/Helper/CatalystUtils.pm index aefddceb..e925cd45 100644 --- a/src/lib/Hydra/Helper/CatalystUtils.pm +++ b/src/lib/Hydra/Helper/CatalystUtils.pm @@ -15,7 +15,7 @@ our @EXPORT = qw( trim getLatestFinishedEval parseJobsetName - $pathCompRE $relPathRE $relNameRE $projectNameRE $jobsetNameRE $jobNameRE $systemRE + $pathCompRE $relPathRE $relNameRE $projectNameRE $jobsetNameRE $jobNameRE $systemRE $userNameRE @buildListColumns ); @@ -171,6 +171,7 @@ Readonly our $projectNameRE => "(?:[A-Za-z_][A-Za-z0-9-_]*)"; Readonly our $jobsetNameRE => "(?:[A-Za-z_][A-Za-z0-9-_]*)"; Readonly our $jobNameRE => "(?:$attrNameRE(?:\\.$attrNameRE)*)"; Readonly our $systemRE => "(?:[a-z0-9_]+-[a-z0-9_]+)"; +Readonly our $userNameRE => "(?:[a-z][a-z0-9_\.]*)"; sub parseJobsetName { diff --git a/src/root/layout.tt b/src/root/layout.tt index 3fabfa58..2a43fbca 100644 --- a/src/root/layout.tt +++ b/src/root/layout.tt @@ -119,6 +119,11 @@

[% flashMsg %]

[% END %] + [% IF successMsg %] +
+

[% successMsg %]

+ [% END %] + [% IF errorMsg %]

Error: [% errorMsg %]

diff --git a/src/root/login.tt b/src/root/login.tt index 5ac6eec3..6bb7368f 100644 --- a/src/root/login.tt +++ b/src/root/login.tt @@ -8,6 +8,11 @@ You can logout here.

[% ELSE %] +

Don't have an account yet? Please register first.

+ +
+
diff --git a/src/root/topbar.tt b/src/root/topbar.tt index 1e95d873..42b40ee9 100644 --- a/src/root/topbar.tt +++ b/src/root/topbar.tt @@ -202,6 +202,7 @@