Allow users to edit their own settings
Also, don't use the flash anymore for going back to the referer.
This commit is contained in:
@ -24,16 +24,6 @@ sub api : Chained('/') PathPart('api') CaptureArgs(0) {
|
||||
}
|
||||
|
||||
|
||||
sub end : ActionClass('RenderView') {
|
||||
my ($self, $c) = @_;
|
||||
if (scalar @{$c->error}) {
|
||||
$c->stash->{json}->{error} = join "\n", @{$c->error};
|
||||
$c->forward('View::JSON');
|
||||
$c->clear_errors;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sub projectToHash {
|
||||
my ($project) = @_;
|
||||
return {
|
||||
@ -317,8 +307,6 @@ sub push : Chained('api') PathPart('push') Args(0) {
|
||||
, where => \ [ 'exists (select 1 from JobsetInputAlts where project = me.project and jobset = me.name and value = ?)', [ 'value', $r ] ]
|
||||
});
|
||||
}
|
||||
|
||||
$c->forward('View::JSON');
|
||||
}
|
||||
|
||||
|
||||
@ -326,19 +314,17 @@ sub push_github : Chained('api') PathPart('push-github') Args(0) {
|
||||
my ($self, $c) = @_;
|
||||
|
||||
$c->{stash}->{json}->{jobsetsTriggered} = [];
|
||||
|
||||
|
||||
my $in = decode_json $c->req->body_params->{payload};
|
||||
my $owner = $in->{repository}->{owner}->{name} or die;
|
||||
my $repo = $in->{repository}->{name} or die;
|
||||
print STDERR "got push from GitHub repository $owner/$repo\n";
|
||||
|
||||
|
||||
triggerJobset($self, $c, $_) foreach $c->model('DB::Jobsets')->search(
|
||||
{ 'project.enabled' => 1, 'me.enabled' => 1 },
|
||||
{ join => 'project'
|
||||
, where => \ [ 'exists (select 1 from JobsetInputAlts where project = me.project and jobset = me.name and value like ?)', [ 'value', "%github.com%/$owner/$repo.git%" ] ]
|
||||
});
|
||||
|
||||
$c->forward('View::JSON');
|
||||
}
|
||||
|
||||
|
||||
|
@ -8,11 +8,6 @@ use Hydra::Helper::CatalystUtils;
|
||||
use Hydra::Helper::AddBuilds;
|
||||
use Data::Dump qw(dump);
|
||||
use Digest::SHA1 qw(sha1_hex);
|
||||
use Crypt::RandPasswd;
|
||||
use Sys::Hostname::Long;
|
||||
use Email::Simple;
|
||||
use Email::Sender::Simple qw(sendmail);
|
||||
use Email::Sender::Transport::SMTP;
|
||||
use Config::General;
|
||||
|
||||
|
||||
@ -52,146 +47,13 @@ sub admin : Chained('/') PathPart('admin') CaptureArgs(0) {
|
||||
}
|
||||
|
||||
|
||||
sub updateUser {
|
||||
my ($c, $user) = @_;
|
||||
|
||||
my $username = trim $c->request->params->{"username"};
|
||||
my $fullname = trim $c->request->params->{"fullname"};
|
||||
my $emailaddress = trim $c->request->params->{"emailaddress"};
|
||||
my $emailonerror = trim $c->request->params->{"emailonerror"};
|
||||
my $roles = $c->request->params->{"roles"} ;
|
||||
|
||||
$user->update(
|
||||
{ fullname => $fullname
|
||||
, emailaddress => $emailaddress
|
||||
, emailonerror => $emailonerror
|
||||
});
|
||||
$user->userroles->delete_all;
|
||||
if(ref($roles) eq 'ARRAY') {
|
||||
for my $s (@$roles) {
|
||||
$user->userroles->create({ role => $s}) ;
|
||||
}
|
||||
} else {
|
||||
$user->userroles->create({ role => $roles}) if defined $roles ;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sub create_user : Chained('admin') PathPart('create-user') Args(0) {
|
||||
my ($self, $c) = @_;
|
||||
|
||||
requireAdmin($c);
|
||||
|
||||
$c->stash->{template} = 'user.tt';
|
||||
$c->stash->{edit} = 1;
|
||||
$c->stash->{create} = 1;
|
||||
}
|
||||
|
||||
|
||||
sub create_user_submit : Chained('admin') PathPart('create-user/submit') Args(0) {
|
||||
my ($self, $c) = @_;
|
||||
|
||||
my $username = trim $c->request->params->{username};
|
||||
|
||||
txn_do($c->model('DB')->schema, sub {
|
||||
my $user = $c->model('DB::Users')->create(
|
||||
{username => $username, emailaddress => "", password => ""});
|
||||
updateUser($c, $user);
|
||||
});
|
||||
|
||||
$c->res->redirect("/admin/users");
|
||||
}
|
||||
|
||||
|
||||
sub user : Chained('admin') PathPart('user') CaptureArgs(1) {
|
||||
my ($self, $c, $username) = @_;
|
||||
|
||||
requireAdmin($c);
|
||||
|
||||
my $user = $c->model('DB::Users')->find($username)
|
||||
or notFound($c, "User $username doesn't exist.");
|
||||
|
||||
$c->stash->{user} = $user;
|
||||
}
|
||||
|
||||
|
||||
sub users : Chained('admin') PathPart('users') Args(0) {
|
||||
my ($self, $c) = @_;
|
||||
$c->stash->{users} = [$c->model('DB::Users')->search({}, {order_by => "username"})];
|
||||
|
||||
$c->stash->{template} = 'users.tt';
|
||||
}
|
||||
|
||||
|
||||
sub user_edit : Chained('user') PathPart('edit') Args(0) {
|
||||
my ($self, $c) = @_;
|
||||
|
||||
$c->stash->{template} = 'user.tt';
|
||||
$c->stash->{edit} = 1;
|
||||
}
|
||||
|
||||
|
||||
sub user_edit_submit : Chained('user') PathPart('submit') Args(0) {
|
||||
my ($self, $c) = @_;
|
||||
requirePost($c);
|
||||
|
||||
txn_do($c->model('DB')->schema, sub {
|
||||
if (($c->request->params->{submit} || "") eq "delete") {
|
||||
$c->stash->{user}->delete;
|
||||
} else {
|
||||
updateUser($c, $c->stash->{user});
|
||||
}
|
||||
});
|
||||
|
||||
$c->res->redirect("/admin/users");
|
||||
}
|
||||
|
||||
|
||||
sub sendemail {
|
||||
my ($to, $subject, $body) = @_;
|
||||
|
||||
my $url = hostname_long;
|
||||
my $sender = ($ENV{'USER'} || "hydra") . "@" . $url;
|
||||
|
||||
my $email = Email::Simple->create(
|
||||
header => [
|
||||
To => $to,
|
||||
From => "Hydra <$sender>",
|
||||
Subject => $subject
|
||||
],
|
||||
body => $body
|
||||
);
|
||||
|
||||
sendmail($email);
|
||||
}
|
||||
|
||||
|
||||
sub reset_password : Chained('user') PathPart('reset-password') Args(0) {
|
||||
my ($self, $c) = @_;
|
||||
|
||||
# generate password
|
||||
my $password = Crypt::RandPasswd->word(8,10);
|
||||
|
||||
# calculate hash
|
||||
my $hashed = sha1_hex($password);
|
||||
|
||||
$c->stash->{user}-> update({ password => $hashed}) ;
|
||||
|
||||
# send email
|
||||
|
||||
sendemail(
|
||||
$c->stash->{user}->emailaddress,
|
||||
"New password for Hydra",
|
||||
"Hi,\n\n".
|
||||
"Your password has been reset. Your new password is '$password'.\n".
|
||||
"You can change your password at " . $c->config()->{'base_uri'} . "/change-password .\n".
|
||||
"With regards, Hydra\n"
|
||||
);
|
||||
|
||||
$c->res->redirect("/admin/users");
|
||||
}
|
||||
|
||||
|
||||
sub machines : Chained('admin') PathPart('machines') Args(0) {
|
||||
my ($self, $c) = @_;
|
||||
$c->stash->{machines} = [$c->model('DB::BuildMachines')->search({}, {order_by => "hostname"})];
|
||||
@ -312,6 +174,7 @@ sub machine_enable : Chained('machine') PathPart('enable') Args(0) {
|
||||
$c->res->redirect("/admin/machines");
|
||||
}
|
||||
|
||||
|
||||
sub machine_disable : Chained('machine') PathPart('disable') Args(0) {
|
||||
my ($self, $c) = @_;
|
||||
$c->stash->{machine}->update({ enabled => 0});
|
||||
|
@ -156,7 +156,15 @@ sub default :Path {
|
||||
sub end : ActionClass('RenderView') {
|
||||
my ($self, $c) = @_;
|
||||
|
||||
if (scalar @{$c->error}) {
|
||||
if (defined $c->stash->{json}) {
|
||||
if (scalar @{$c->error}) {
|
||||
$c->stash->{json}->{error} = join "\n", @{$c->error};
|
||||
$c->clear_errors;
|
||||
}
|
||||
$c->forward('View::JSON');
|
||||
}
|
||||
|
||||
elsif (scalar @{$c->error}) {
|
||||
$c->stash->{template} = 'error.tt';
|
||||
$c->stash->{errors} = $c->error;
|
||||
$c->response->status(500) if $c->response->status == 200;
|
||||
@ -216,33 +224,6 @@ sub narinfo :LocalRegex('^([a-z0-9]+).narinfo$') :Args(0) {
|
||||
}
|
||||
|
||||
|
||||
sub change_password : Path('change-password') :Args(0) {
|
||||
my ($self, $c) = @_;
|
||||
|
||||
requireLogin($c) if !$c->user_exists;
|
||||
|
||||
$c->stash->{template} = 'change-password.tt';
|
||||
}
|
||||
|
||||
|
||||
sub change_password_submit : Path('change-password/submit') : Args(0) {
|
||||
my ($self, $c) = @_;
|
||||
|
||||
requireLogin($c) if !$c->user_exists;
|
||||
|
||||
my $password = $c->request->params->{"password"};
|
||||
my $password_check = $c->request->params->{"password_check"};
|
||||
print STDERR "$password \n";
|
||||
print STDERR "$password_check \n";
|
||||
error($c, "Passwords did not match, go back and try again!") if $password ne $password_check;
|
||||
|
||||
my $hashed = sha1_hex($password);
|
||||
$c->user->update({ password => $hashed}) ;
|
||||
|
||||
$c->res->redirect("/");
|
||||
}
|
||||
|
||||
|
||||
sub logo :Local {
|
||||
my ($self, $c) = @_;
|
||||
my $path = $ENV{"HYDRA_LOGO"} or die("Logo not set!");
|
||||
|
@ -1,8 +1,10 @@
|
||||
package Hydra::Controller::User;
|
||||
|
||||
use utf8;
|
||||
use strict;
|
||||
use warnings;
|
||||
use base 'Catalyst::Controller';
|
||||
use Crypt::RandPasswd;
|
||||
use Digest::SHA1 qw(sha1_hex);
|
||||
use Hydra::Helper::Nix;
|
||||
use Hydra::Helper::CatalystUtils;
|
||||
@ -17,23 +19,17 @@ sub login :Local {
|
||||
my $username = $c->request->params->{username} || "";
|
||||
my $password = $c->request->params->{password} || "";
|
||||
|
||||
if ($username eq "" && $password eq "" && !defined $c->flash->{referer}) {
|
||||
if ($username eq "" && $password eq "" && !defined $c->session->{referer}) {
|
||||
my $baseurl = $c->uri_for('/');
|
||||
my $refurl = $c->request->referer;
|
||||
$c->flash->{referer} = $refurl if $refurl =~ m/^($baseurl)/;
|
||||
my $referer = $c->request->referer;
|
||||
$c->session->{referer} = $referer if defined $referer && $referer =~ 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;
|
||||
}
|
||||
backToReferer($c) if $c->authenticate({username => $username, password => $password});
|
||||
$c->stash->{errorMsg} = "Bad username or password.";
|
||||
}
|
||||
|
||||
$c->keep_flash("referer");
|
||||
|
||||
$c->stash->{template} = 'login.tt';
|
||||
}
|
||||
|
||||
@ -51,6 +47,18 @@ sub captcha :Local Args(0) {
|
||||
}
|
||||
|
||||
|
||||
sub isValidPassword {
|
||||
my ($password) = @_;
|
||||
return length($password) >= 6;
|
||||
}
|
||||
|
||||
|
||||
sub setPassword {
|
||||
my ($user, $password) = @_;
|
||||
$user->update({ password => sha1_hex($password) });
|
||||
}
|
||||
|
||||
|
||||
sub register :Local Args(0) {
|
||||
my ($self, $c) = @_;
|
||||
|
||||
@ -81,7 +89,7 @@ sub register :Local Args(0) {
|
||||
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;
|
||||
unless isValidPassword($password);
|
||||
|
||||
return fail($c, "The passwords you specified did not match.")
|
||||
if $password ne trim $c->req->params->{password2};
|
||||
@ -90,22 +98,111 @@ sub register :Local Args(0) {
|
||||
my $user = $c->model('DB::Users')->create(
|
||||
{ username => $userName
|
||||
, fullname => $fullName
|
||||
, password => sha1_hex($password)
|
||||
, password => "!"
|
||||
, emailaddress => "",
|
||||
});
|
||||
setPassword($user, $password);
|
||||
});
|
||||
|
||||
$c->authenticate({username => $userName, password => $password})
|
||||
or error($c, "Unable to authenticate the new user!");
|
||||
unless ($c->user_exists) {
|
||||
$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('/'));
|
||||
backToReferer($c);
|
||||
}
|
||||
|
||||
|
||||
sub preferences :Local Args(0) {
|
||||
sub user :Chained('/') PathPart('user') CaptureArgs(1) {
|
||||
my ($self, $c, $userName) = @_;
|
||||
|
||||
requireLogin($c) if !$c->user_exists;
|
||||
|
||||
error($c, "You do not have permission to edit other users.")
|
||||
if $userName ne $c->user->username && !isAdmin($c);
|
||||
|
||||
$c->stash->{user} = $c->model('DB::Users')->find($userName)
|
||||
or notFound($c, "User $userName doesn't exist.");
|
||||
}
|
||||
|
||||
|
||||
sub deleteUser {
|
||||
my ($self, $c, $user) = @_;
|
||||
my ($project) = $c->model('DB::Projects')->search({ owner => $user->username });
|
||||
error($c, "User " . $user->username . " is still owner of project " . $project->name . ".")
|
||||
if defined $project;
|
||||
$c->logout() if $user->username eq $c->user->username;
|
||||
$user->delete;
|
||||
}
|
||||
|
||||
|
||||
sub edit :Chained('user') Args(0) {
|
||||
my ($self, $c) = @_;
|
||||
error($c, "Not implemented.");
|
||||
|
||||
my $user = $c->stash->{user};
|
||||
|
||||
$c->stash->{template} = 'user.tt';
|
||||
|
||||
$c->session->{referer} = $c->request->referer if !defined $c->session->{referer};
|
||||
|
||||
if ($c->request->method ne "POST") {
|
||||
$c->stash->{fullname} = $user->fullname;
|
||||
$c->stash->{emailonerror} = $user->emailonerror;
|
||||
return;
|
||||
}
|
||||
|
||||
if (($c->request->params->{submit} // "") eq "delete") {
|
||||
deleteUser($self, $c, $user);
|
||||
backToReferer($c);
|
||||
}
|
||||
|
||||
if (($c->request->params->{submit} // "") eq "reset-password") {
|
||||
$c->stash->{json} = {};
|
||||
error($c, "No email address is set for this user.")
|
||||
unless $user->emailaddress;
|
||||
my $password = Crypt::RandPasswd->word(8,10);
|
||||
setPassword($user, $password);
|
||||
sendEmail($c,
|
||||
$user->emailaddress,
|
||||
"Hydra password reset",
|
||||
"Hi,\n\n".
|
||||
"Your password has been reset. Your new password is '$password'.\n\n".
|
||||
"You can change your password at " . $c->uri_for($self->action_for('edit'), [$user->username]) . ".\n\n".
|
||||
"With regards,\n\nHydra.\n"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
my $fullName = trim $c->req->params->{fullname};
|
||||
|
||||
txn_do($c->model('DB')->schema, sub {
|
||||
|
||||
error($c, "Your must specify your full name.") if $fullName eq "";
|
||||
|
||||
$user->update(
|
||||
{ fullname => $fullName
|
||||
, emailonerror => $c->request->params->{"emailonerror"} ? 1 : 0
|
||||
});
|
||||
|
||||
my $password = $c->req->params->{password} // "";
|
||||
if ($password ne "") {
|
||||
error($c, "You must specify a password of at least 6 characters.")
|
||||
unless isValidPassword($password);
|
||||
error($c, "The passwords you specified did not match.")
|
||||
if $password ne trim $c->req->params->{password2};
|
||||
setPassword($user, $password);
|
||||
}
|
||||
|
||||
if (isAdmin($c)) {
|
||||
$user->userroles->delete_all;
|
||||
$user->userroles->create({ role => $_})
|
||||
foreach paramToList($c, "roles");
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
backToReferer($c);
|
||||
}
|
||||
|
||||
|
||||
|
@ -4,6 +4,10 @@ use utf8;
|
||||
use strict;
|
||||
use Exporter;
|
||||
use Readonly;
|
||||
use Email::Simple;
|
||||
use Email::Sender::Simple qw(sendmail);
|
||||
use Email::Sender::Transport::SMTP;
|
||||
use Sys::Hostname::Long;
|
||||
use Nix::Store;
|
||||
use Hydra::Helper::Nix;
|
||||
|
||||
@ -15,6 +19,9 @@ our @EXPORT = qw(
|
||||
trim
|
||||
getLatestFinishedEval
|
||||
parseJobsetName
|
||||
sendEmail
|
||||
paramToList
|
||||
backToReferer
|
||||
$pathCompRE $relPathRE $relNameRE $projectNameRE $jobsetNameRE $jobNameRE $systemRE $userNameRE
|
||||
@buildListColumns
|
||||
);
|
||||
@ -97,8 +104,17 @@ sub notFound {
|
||||
}
|
||||
|
||||
|
||||
sub backToReferer {
|
||||
my ($c) = @_;
|
||||
$c->response->redirect($c->session->{referer} || $c->uri_for('/'));
|
||||
$c->session->{referer} = undef;
|
||||
$c->detach;
|
||||
}
|
||||
|
||||
|
||||
sub requireLogin {
|
||||
my ($c) = @_;
|
||||
$c->session->{referer} = $c->request->uri;
|
||||
$c->response->redirect($c->uri_for('/login'));
|
||||
$c->detach; # doesn't return
|
||||
}
|
||||
@ -162,6 +178,39 @@ sub getLatestFinishedEval {
|
||||
}
|
||||
|
||||
|
||||
sub sendEmail {
|
||||
my ($c, $to, $subject, $body) = @_;
|
||||
|
||||
my $sender = $c->config->{'notification_sender'} ||
|
||||
(($ENV{'USER'} || "hydra") . "@" . hostname_long);
|
||||
|
||||
my $email = Email::Simple->create(
|
||||
header => [
|
||||
To => $to,
|
||||
From => "Hydra <$sender>",
|
||||
Subject => $subject
|
||||
],
|
||||
body => $body
|
||||
);
|
||||
|
||||
print STDERR "Sending email:\n", $email->as_string if $ENV{'HYDRA_MAIL_TEST'};
|
||||
|
||||
sendmail($email);
|
||||
}
|
||||
|
||||
|
||||
# Catalyst request parameters can be an array or a scalar or
|
||||
# undefined, making them annoying to handle. So this utility function
|
||||
# always returns a request parameter as a list.
|
||||
sub paramToList {
|
||||
my ($c, $name) = @_;
|
||||
my $x = $c->request->params->{$name};
|
||||
return () unless defined $x;
|
||||
return @$x if ref($x) eq 'ARRAY';
|
||||
return ($x);
|
||||
}
|
||||
|
||||
|
||||
# Security checking of filenames.
|
||||
Readonly our $pathCompRE => "(?:[A-Za-z0-9-\+\._\$][A-Za-z0-9-\+\._\$]*)";
|
||||
Readonly our $relPathRE => "(?:$pathCompRE(?:/$pathCompRE)*)";
|
||||
|
Reference in New Issue
Block a user