forked from lix-project/hydra
Add user registration
This commit is contained in:
parent
180068605a
commit
e8cbcb50ac
1
deps.nix
1
deps.nix
|
@ -5,6 +5,7 @@ with pkgs;
|
||||||
[ perlPackages.CatalystAuthenticationStoreDBIxClass
|
[ perlPackages.CatalystAuthenticationStoreDBIxClass
|
||||||
perlPackages.CatalystPluginAccessLog
|
perlPackages.CatalystPluginAccessLog
|
||||||
perlPackages.CatalystPluginAuthorizationRoles
|
perlPackages.CatalystPluginAuthorizationRoles
|
||||||
|
perlPackages.CatalystPluginCaptcha
|
||||||
perlPackages.CatalystPluginSessionStateCookie
|
perlPackages.CatalystPluginSessionStateCookie
|
||||||
perlPackages.CatalystPluginSessionStoreFastMmap
|
perlPackages.CatalystPluginSessionStoreFastMmap
|
||||||
perlPackages.CatalystPluginStackTrace
|
perlPackages.CatalystPluginStackTrace
|
||||||
|
|
|
@ -13,7 +13,8 @@ use Catalyst qw/ConfigLoader
|
||||||
Session
|
Session
|
||||||
Session::Store::FastMmap
|
Session::Store::FastMmap
|
||||||
Session::State::Cookie
|
Session::State::Cookie
|
||||||
AccessLog/,
|
AccessLog
|
||||||
|
Captcha/,
|
||||||
'-Log=warn,fatal,error';
|
'-Log=warn,fatal,error';
|
||||||
|
|
||||||
our $VERSION = '0.01';
|
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]',
|
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();
|
__PACKAGE__->setup();
|
||||||
|
|
|
@ -87,6 +87,7 @@ sub create_user : Chained('admin') PathPart('create-user') Args(0) {
|
||||||
$c->stash->{create} = 1;
|
$c->stash->{create} = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
sub create_user_submit : Chained('admin') PathPart('create-user/submit') Args(0) {
|
sub create_user_submit : Chained('admin') PathPart('create-user/submit') Args(0) {
|
||||||
my ($self, $c) = @_;
|
my ($self, $c) = @_;
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,9 @@ sub begin :Private {
|
||||||
$c->stash->{nixVersion} = $ENV{"NIX_RELEASE"} || "<devel>";
|
$c->stash->{nixVersion} = $ENV{"NIX_RELEASE"} || "<devel>";
|
||||||
$c->stash->{curTime} = time;
|
$c->stash->{curTime} = time;
|
||||||
$c->stash->{logo} = $ENV{"HYDRA_LOGO"} ? "/logo" : "";
|
$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") {
|
if (scalar(@args) == 0 || $args[0] ne "static") {
|
||||||
$c->stash->{nrRunningBuilds} = $c->model('DB::Builds')->search({ finished => 0, busy => 1 }, {})->count();
|
$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 {
|
sub queue :Local {
|
||||||
my ($self, $c) = @_;
|
my ($self, $c) = @_;
|
||||||
$c->stash->{template} = 'queue.tt';
|
$c->stash->{template} = 'queue.tt';
|
||||||
$c->stash->{queue} = [$c->model('DB::Builds')->search(
|
$c->stash->{queue} = [$c->model('DB::Builds')->search(
|
||||||
{finished => 0}, { join => ['project'], order_by => ["priority DESC", "timestamp"], columns => [@buildListColumns], '+select' => ['project.enabled'], '+as' => ['enabled'] })];
|
{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};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
112
src/lib/Hydra/Controller/User.pm
Normal file
112
src/lib/Hydra/Controller/User.pm
Normal file
|
@ -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 <tt>$userName</tt> 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;
|
|
@ -15,7 +15,7 @@ our @EXPORT = qw(
|
||||||
trim
|
trim
|
||||||
getLatestFinishedEval
|
getLatestFinishedEval
|
||||||
parseJobsetName
|
parseJobsetName
|
||||||
$pathCompRE $relPathRE $relNameRE $projectNameRE $jobsetNameRE $jobNameRE $systemRE
|
$pathCompRE $relPathRE $relNameRE $projectNameRE $jobsetNameRE $jobNameRE $systemRE $userNameRE
|
||||||
@buildListColumns
|
@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 $jobsetNameRE => "(?:[A-Za-z_][A-Za-z0-9-_]*)";
|
||||||
Readonly our $jobNameRE => "(?:$attrNameRE(?:\\.$attrNameRE)*)";
|
Readonly our $jobNameRE => "(?:$attrNameRE(?:\\.$attrNameRE)*)";
|
||||||
Readonly our $systemRE => "(?:[a-z0-9_]+-[a-z0-9_]+)";
|
Readonly our $systemRE => "(?:[a-z0-9_]+-[a-z0-9_]+)";
|
||||||
|
Readonly our $userNameRE => "(?:[a-z][a-z0-9_\.]*)";
|
||||||
|
|
||||||
|
|
||||||
sub parseJobsetName {
|
sub parseJobsetName {
|
||||||
|
|
|
@ -119,6 +119,11 @@
|
||||||
<p class="btn-info btn-large">[% flashMsg %]</p>
|
<p class="btn-info btn-large">[% flashMsg %]</p>
|
||||||
[% END %]
|
[% END %]
|
||||||
|
|
||||||
|
[% IF successMsg %]
|
||||||
|
<br />
|
||||||
|
<p class="btn-success btn-large">[% successMsg %]</p>
|
||||||
|
[% END %]
|
||||||
|
|
||||||
[% IF errorMsg %]
|
[% IF errorMsg %]
|
||||||
<br />
|
<br />
|
||||||
<p class="btn-warning btn-large">Error: [% errorMsg %]</p>
|
<p class="btn-warning btn-large">Error: [% errorMsg %]</p>
|
||||||
|
|
|
@ -8,6 +8,11 @@ You can <a href="[% c.uri_for('/logout') %]">logout</a> here.
|
||||||
</p>
|
</p>
|
||||||
[% ELSE %]
|
[% ELSE %]
|
||||||
|
|
||||||
|
<p>Don't have an account yet? Please <a href="[%
|
||||||
|
c.uri_for('/register') %]">register</a> first.</p>
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
|
||||||
<form class="form-horizontal" method="post" action="[% c.uri_for('/login') %]">
|
<form class="form-horizontal" method="post" action="[% c.uri_for('/login') %]">
|
||||||
|
|
||||||
<fieldset>
|
<fieldset>
|
||||||
|
|
|
@ -202,6 +202,7 @@
|
||||||
<ul class="nav" id="top-menu">
|
<ul class="nav" id="top-menu">
|
||||||
|
|
||||||
[% IF c.user_exists %]
|
[% IF c.user_exists %]
|
||||||
|
[% INCLUDE makeLink uri = c.uri_for(c.controller('Root').action_for('preferences')) title = "Preferences" %]
|
||||||
[% INCLUDE makeLink uri = c.uri_for(c.controller('Root').action_for('logout')) title = "Sign out" %]
|
[% INCLUDE makeLink uri = c.uri_for(c.controller('Root').action_for('logout')) title = "Sign out" %]
|
||||||
[% ELSE %]
|
[% ELSE %]
|
||||||
[% INCLUDE makeLink uri = c.uri_for(c.controller('Root').action_for('login')) title = "Sign in" %]
|
[% INCLUDE makeLink uri = c.uri_for(c.controller('Root').action_for('login')) title = "Sign in" %]
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
[% WRAPPER layout.tt title=(create ? "New user" : "User $user.username") %]
|
[% WRAPPER layout.tt title=(create ? "Register new user" : "User $user.username") %]
|
||||||
[% PROCESS common.tt %]
|
[% PROCESS common.tt %]
|
||||||
|
|
||||||
[% BLOCK roleoption %]
|
[% BLOCK roleoption %]
|
||||||
|
@ -14,33 +14,60 @@
|
||||||
>[% role %]</option>
|
>[% role %]</option>
|
||||||
[% END %]
|
[% END %]
|
||||||
|
|
||||||
<form class="form-horizontal" action="[% IF create %][% c.uri_for('/admin/create-user/submit') %][% ELSE %][% c.uri_for('/admin/user' user.username 'submit') %][% END %]" method="post">
|
<form class="form-horizontal" method="post">
|
||||||
|
|
||||||
|
<fieldset>
|
||||||
|
|
||||||
<fieldset>
|
|
||||||
[% IF create %]
|
[% IF create %]
|
||||||
<div class="control-group">
|
<div class="control-group">
|
||||||
<label class="control-label">User name</label>
|
<label class="control-label">User name</label>
|
||||||
<div class="controls">
|
<div class="controls">
|
||||||
<input type="text" class="span3" name="username" value=""></input>
|
<input type="text" class="span3" name="username" [% HTML.attributes(value => username) %]></input>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
[% END %]
|
[% END %]
|
||||||
|
|
||||||
<div class="control-group">
|
<div class="control-group">
|
||||||
<label class="control-label">Full name</label>
|
<label class="control-label">Full name</label>
|
||||||
<div class="controls">
|
<div class="controls">
|
||||||
<input type="text" class="span3" name="fullname" [% HTML.attributes(value => user.fullname) %]></input>
|
<input type="text" class="span3" name="fullname" [% HTML.attributes(value => fullname) %]></input>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="control-group">
|
||||||
|
<label class="control-label">Password</label>
|
||||||
|
<div class="controls">
|
||||||
|
<input type="password" class="span3" name="password" value=""></input>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="control-group">
|
||||||
|
<label class="control-label">Confirm password</label>
|
||||||
|
<div class="controls">
|
||||||
|
<input type="password" class="span3" name="password2" value=""></input>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!--
|
||||||
<div class="control-group">
|
<div class="control-group">
|
||||||
<label class="control-label">Email</label>
|
<label class="control-label">Email</label>
|
||||||
<div class="controls">
|
<div class="controls">
|
||||||
<input type="text" class="span3" name="emailaddress" [% HTML.attributes(value => user.emailaddress) %]></input>
|
<input type="text" class="span3" name="emailaddress" [% HTML.attributes(value => user.emailaddress) %]></input>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
-->
|
||||||
|
|
||||||
|
[% IF !create %]
|
||||||
<div class="control-group">
|
<div class="control-group">
|
||||||
<label class="control-label">Evaluation error notifications</label>
|
<div class="controls">
|
||||||
[% INCLUDE renderSelection param="emailonerror" curValue=user.emailonerror radiobuttons=1 options={"1" = "Yes", "0" = "No"} %]
|
<label class="checkbox">
|
||||||
|
<input type="checkbox" name="enabled" [% IF 1; 'checked="checked"'; END %]></input>Receive evaluation error notifications
|
||||||
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
[% END %]
|
||||||
|
|
||||||
|
[% IF !create && c.check_user_roles('admin') %]
|
||||||
<div class="control-group">
|
<div class="control-group">
|
||||||
<label class="control-label">Roles</label>
|
<label class="control-label">Roles</label>
|
||||||
<div class="controls">
|
<div class="controls">
|
||||||
|
@ -50,6 +77,22 @@
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
[% END %]
|
||||||
|
|
||||||
|
[% IF create %]
|
||||||
|
<div class="control-group">
|
||||||
|
<div class="controls">
|
||||||
|
<img src="[% c.uri_for('/captcha') %]" alt="CAPTCHA"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="control-group">
|
||||||
|
<label class="control-label">Type the digits shown in the image above</label>
|
||||||
|
<div class="controls">
|
||||||
|
<input type="text" class="span3" name="captcha" value=""></input>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
[% END %]
|
||||||
|
|
||||||
<div class="form-actions">
|
<div class="form-actions">
|
||||||
<button type="submit" class="btn btn-primary">
|
<button type="submit" class="btn btn-primary">
|
||||||
|
|
Loading…
Reference in a new issue