From 3baaa399714f45fc88e2b785a958d190cfeff797 Mon Sep 17 00:00:00 2001 From: "Kyle J. McKay" Date: Sun, 7 Jul 2013 10:31:20 -0700 Subject: [PATCH] Support web removal of users A confirmation code is sent similarly to editing a user, but it is distinct so that the edit user code may not be interchanged with the remove user code and vice versa. After entering the correct code, the user is asked to confirm with an additional button click after being shown the list of projects, if any, the user will be removed from. --- Girocco/Project.pm | 22 ++++---- Girocco/User.pm | 11 ++-- cgi/delproj.cgi | 4 +- cgi/deluser.cgi | 157 +++++++++++++++++++++++++++++++++++++++++++++++++++++ cgi/editproj.cgi | 4 +- cgi/edituser.cgi | 8 ++- html/girocco.css | 39 +++++++++++++ taskd/clone.sh | 2 +- 8 files changed, 223 insertions(+), 24 deletions(-) create mode 100755 cgi/deluser.cgi diff --git a/Girocco/Project.pm b/Girocco/Project.pm index eab598d..f6ccc16 100644 --- a/Girocco/Project.pm +++ b/Girocco/Project.pm @@ -89,7 +89,7 @@ sub _property_fget { return $val; } - open P, $self->_property_path($pname) or return undef; + open P, '<', $self->_property_path($pname) or return undef; my @value =

; close P; my $value = join('', @value); chomp $value; @@ -153,12 +153,12 @@ sub _nofetch_path { sub _nofetch { my $self = shift; my ($nofetch) = @_; - my $np = $self->_nofetch_path; + my $nf = $self->_nofetch_path; if ($nofetch) { - open X, '>'.$np or die "nofetch failed: $!"; + open X, '>', $nf or die "nofetch failed: $!"; close X; } else { - unlink $np or die "yesfetch failed: $!"; + unlink $nf or die "yesfetch failed: $!"; } } @@ -182,7 +182,7 @@ sub _clonep { my ($nofetch) = @_; my $np = $self->_clonep_path; if ($nofetch) { - open X, '>'.$np or die "clonep failed: $!"; + open X, '>', $np or die "clonep failed: $!"; close X; } else { unlink $np or die "clonef failed: $!"; @@ -202,14 +202,14 @@ sub _alternates_setup { # relative path in alternates - that doesn't work recursively. my $filename = $self->{path}.'/objects/info/alternates'; - open X, '>'.$filename or die "alternates failed: $!"; + open X, '>', $filename or die "alternates failed: $!"; print X "$forkee_path/objects\n"; close X; chmod 0664, $filename or warn "cannot chmod $filename: $!"; if ($Girocco::Config::httppullurl) { $filename = $self->{path}.'/objects/info/http-alternates'; - open X, '>'.$filename or die "http-alternates failed: $!"; + open X, '>', $filename or die "http-alternates failed: $!"; my $upfork = $forkee_name; do { print X "$Girocco::Config::httppullurl/$upfork.git/objects\n"; } while ($upfork =~ s#/?.+?$## and $upfork); # close X; @@ -275,8 +275,8 @@ sub _hook_path { sub _hook_install { my $self = shift; my ($name) = @_; - open SRC, "$Girocco::Config::basedir/hooks/$name" or die "cannot open hook $name: $!"; - open DST, '>'.$self->_hook_path($name) or die "cannot open hook $name for writing: $!"; + open SRC, '<', "$Girocco::Config::basedir/hooks/$name" or die "cannot open hook $name: $!"; + open DST, '>', $self->_hook_path($name) or die "cannot open hook $name for writing: $!"; while () { print DST $_; } close DST; close SRC; @@ -319,7 +319,7 @@ sub load { my $class = shift; my $name = shift || ''; - open F, jailed_file("/etc/group") or die "project load failed: $!"; + open F, '<', jailed_file("/etc/group") or die "project load failed: $!"; while () { chomp; @_ = split /:+/; @@ -727,7 +727,7 @@ sub get_full_list { my $class = shift; my @projects; - open F, jailed_file("/etc/group") or die "getting project list failed: $!"; + open F, '<', jailed_file("/etc/group") or die "getting project list failed: $!"; while () { chomp; @_ = split /:+/; diff --git a/Girocco/User.pm b/Girocco/User.pm index 5322890..1eeaad9 100644 --- a/Girocco/User.pm +++ b/Girocco/User.pm @@ -99,7 +99,7 @@ sub _sshkey_path { sub _sshkey_load { my $self = shift; - open F, "<".jailed_file($self->_sshkey_path) or die "sshkey load failed: $!"; + open F, '<', jailed_file($self->_sshkey_path) or die "sshkey load failed: $!"; my @keys = (); my $auth = ''; my $authtype = ''; @@ -131,13 +131,14 @@ sub _trimkeys { sub _sshkey_save { my $self = shift; $self->{keys} = _trimkeys($self->{keys} || ''); - open F, ">".jailed_file($self->_sshkey_path) or die "sshkey failed: $!"; + open F, '>', jailed_file($self->_sshkey_path) or die "sshkey save failed: $!"; if (defined($self->{auth}) && $self->{auth}) { my $expire = time + 24 * 3600; my $typestr = $self->{authtype} ? uc($self->{authtype}) : 'REPO'; print F "# ${typestr}AUTH $self->{auth} $expire\n"; } - print F $self->{keys}."\n"; + print F $self->{keys}; + print F "\n" if $self->{keys}; close F; chmod 0664, jailed_file($self->_sshkey_path); } @@ -167,7 +168,7 @@ sub load { my $class = shift; my ($name) = @_; - open F, jailed_file("/etc/passwd") or die "user load failed: $!"; + open F, '<', jailed_file("/etc/passwd") or die "user load failed: $!"; while () { chomp; @_ = split /:/; @@ -193,7 +194,7 @@ sub load_by_uid { my $class = shift; my ($uid) = @_; - open F, jailed_file("/etc/passwd") or die "user load failed: $!"; + open F, '<', jailed_file("/etc/passwd") or die "user load failed: $!"; while () { chomp; @_ = split /:/; diff --git a/cgi/delproj.cgi b/cgi/delproj.cgi index 161207c..f903364 100755 --- a/cgi/delproj.cgi +++ b/cgi/delproj.cgi @@ -76,7 +76,7 @@ you have used it.

-

Authorization code:

+

Authorization code:

EOT @@ -100,7 +100,7 @@ EOT
-

Authorization code:

+

Authorization code:

EOT diff --git a/cgi/deluser.cgi b/cgi/deluser.cgi new file mode 100755 index 0000000..8ff55d1 --- /dev/null +++ b/cgi/deluser.cgi @@ -0,0 +1,157 @@ +#!/usr/bin/perl + +# deluser.cgi -- support for user deletion via web +# Copyright (c) 2013 Kyle J. McKay. All rights reserved. +# Portions (c) Petr Baudis and (c) Jan Krueger +# License GPLv2+: GNU GPL version 2 or later. +# www.gnu.org/licenses/gpl-2.0.html +# This is free software: you are free to change and redistribute it. +# There is NO WARRANTY, to the extent permitted by law. + +use strict; +use warnings; + +use lib "."; +use Girocco::CGI; +use Girocco::Config; +use Girocco::User; +use Girocco::Util; + +my $gcgi = Girocco::CGI->new('User Removal'); +my $cgi = $gcgi->cgi; + +unless ($Girocco::Config::manage_users) { + print "

I don't manage users.

"; + exit; +} + +if ($cgi->param('mail')) { + print "

Go away, bot.

"; + exit; +} + +sub _auth_form { + my ($name, $submit) = @_; + print < + +

Authorization code:

+

+ +EOT +} + +if ($cgi->param('name')) { + # submitted, let's see + # FIXME: racy, do a lock + my $name = $gcgi->wparam('name'); + (Girocco::User::valid_name($name) + and Girocco::User::does_exist($name)) + or $gcgi->err("Username is not registered."); + + $gcgi->err_check and exit; + + my $user; + ($user = Girocco::User->load($name)) && valid_email($user->{email}) + or $gcgi->err("Username may not be removed."); + + $gcgi->err_check and exit; + + if (!$cgi->param('auth')) { + my $auth = $user->gen_auth('DEL'); + + # Send auth mail + open(MAIL, '|-', '/usr/bin/mail', '-s', "[$Girocco::Config::name] Account removal authorization", $user->{email}) or + die "Sorry, could not send authorization code: $!"; + print MAIL <You should shortly receive an e-mail containing an authorization code. + Please enter this code below to remove your account. + The code will expire in 24 hours or after you have used it.

"; + _auth_form($name, "'Login'"); + exit; + } else { + $user->{auth} && $user->{authtype} eq 'DEL' or do { + print "There currently isn't any authorization code filed under your account. ". + "Please generate one."; + exit; + }; + + my $auth = $gcgi->wparam('auth'); + if ($auth ne $user->{auth}) { + print '

Invalid authorization code, please re-enter or ". + "generate a new one.

'; + _auth_form($name, "'Login'"); + exit; + } + + my $y0 = $gcgi->wparam('y0') || ''; + my $conf = $gcgi->wparam('confirm') || ''; + if ($y0 ne 'Remove user account' || $conf ne $user->{name}) { + my $blurb1 = '.'; + my $projectsinfo = ''; + my @projects = $user->get_projects; + if (@projects) { + $blurb1 = ' and from the following projects:'; + $projectsinfo = < +EOT + foreach (@projects) { + if (Girocco::Project::does_exist($_)) { + my $proj = Girocco::Project->load($_); + $projectsinfo .= < +EOT + } + } + $projectsinfo .= <

+EOT + } + print <Please confirm that you are going to remove user account '$user->{name}' +from the site$blurb1

$projectsinfo +
+ + + +

+ +EOT + exit; + } + + $user->remove; + print "

User account successfully removed. Have a nice day.

\n"; + exit; + } +} + +print <Here you can request an authorization code to remove your user account.

+ +

Please enter your username below; +we will send you an email with an authorization code +and further instructions.

+ +
+
ProjectDescription
@{[CGI::escapeHTML($proj->{name})]}@{[CGI::escapeHTML($proj->{desc})]}
+ + + +
Login:
Anti-captcha (leave empty!):
+ +EOT diff --git a/cgi/editproj.cgi b/cgi/editproj.cgi index 28279f8..b0a232b 100755 --- a/cgi/editproj.cgi +++ b/cgi/editproj.cgi @@ -89,9 +89,9 @@ You may request an authorization code in order to remove this project from the site.

EOT - } + } print <+ button to enable access for more than a single user at a time.

+

Use the + button to enable access for more than a single user at a time.

EOT } diff --git a/cgi/edituser.cgi b/cgi/edituser.cgi index 443dd00..ffb89fa 100755 --- a/cgi/edituser.cgi +++ b/cgi/edituser.cgi @@ -32,7 +32,7 @@ sub _auth_form { $fields = '' if (!$fields); my $auth = shift; my $authtag = ($auth ? qq() : - qq(

Authorization code:

)); + qq(

Authorization code:

)); print < @@ -182,8 +182,10 @@ my $blurb2 = ''; $blurb2 = ' and download https push user authentication certificate(s)' if $Girocco::Config::httpspushurl; print <Here you can update the email and public SSH key(s) associated with your user account$blurb2. -These public SSH key(s) are required for you to push to projects.

+

Here you may update the email and public SSH key(s) associated with your user account$blurb2. +You may request an authorization +code in order to remove your user account from this site.

+

The public SSH key(s) are required for you to push to projects.

$blurb1 is used for pushing, your SSH key authenticates you - there is no password (though we recommend that your SSH key is password-protected; use ssh-agent to help your fingers). diff --git a/html/girocco.css b/html/girocco.css index 5e3cf78..2093848 100644 --- a/html/girocco.css +++ b/html/girocco.css @@ -1,5 +1,14 @@ /* Girocco CSS */ +.form { + padding: 0; +} + +.projectlist, p, pre { + margin-left: 1ex; + margin-right: 1ex; +} + .formlabel { margin-right: 0.5em; padding-top: 0.3em; @@ -17,3 +26,33 @@ .ctxaction { text-decoration: none; } + +.projectlist { + border-collapse: collapse; + border: none; + background-color: #f8f8f8; +} + +.projectlist th, .projectlist td { + border-color: black; + border-width: thin; + border-left-style: dotted; + padding-left: 0.5em; + padding-top: 0.5ex; + padding-bottom: 0.5ex; +} + +.projectlist th { + text-align: left; + border-bottom-style: dotted; +} + +.projectlist td { + border-top-style: dotted; +} + +.projectlist th:first-child, .projectlist td:first-child { + border-left-style: hidden; + padding-right: 0.5em; + padding-left: 0; +} diff --git a/taskd/clone.sh b/taskd/clone.sh index c38e5a0..3dc324d 100755 --- a/taskd/clone.sh +++ b/taskd/clone.sh @@ -53,7 +53,7 @@ case "$url" in # git svn does not preserve group permissions in the svn subdirectory chmod -R ug+rw,o+r svn ;; - darcs://*) + darcs://*) httpurl="${url/darcs:\/\//http://}" "$cfg_basedir"/bin/darcs-fast-export --export-marks=$(pwd)/dfe-marks "$httpurl" | \ git fast-import --export-marks=$(pwd)/gfi-marks -- 2.11.4.GIT