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.
+
+
+