summaryrefslogtreecommitdiff
path: root/lib/Travelynx/Command/account.pm
blob: 1cd58bd8e75511eab125d6d14015900b73fd0abf (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
package Travelynx::Command::account;

# Copyright (C) 2021 Daniel Friesel
#
# SPDX-License-Identifier: AGPL-3.0-or-later
use Mojo::Base 'Mojolicious::Command';
use Crypt::Eksblowfish::Bcrypt qw(bcrypt en_base64);
use UUID::Tiny                 qw(:std);

has description => 'Add or remove user accounts';

has usage => sub { shift->extract_usage };

sub hash_password {
	my ($password) = @_;
	my @salt_bytes = map { int( rand(255) ) + 1 } ( 1 .. 16 );
	my $salt       = en_base64( pack( 'C[16]', @salt_bytes ) );

	return bcrypt( $password, '$2a$12$' . $salt );
}

sub add_user {
	my ( $self, $name, $email ) = @_;

	my $db = $self->app->pg->db;

	if ( my $error = $self->app->users->is_name_invalid( name => $name ) ) {
		say "Cannot add account '$name': $error";
		die;
	}

	my $token         = "tmp";
	my $password      = substr( create_uuid_as_string(UUID_V4), 0, 18 );
	my $password_hash = hash_password($password);

	my $tx      = $db->begin;
	my $user_id = $self->app->users->add(
		db            => $db,
		name          => $name,
		email         => $email,
		token         => $token,
		password_hash => $password_hash,
	);
	my $success = $self->app->users->verify_registration_token(
		db             => $db,
		uid            => $user_id,
		token          => $token,
		in_transaction => 1,
	);

	if ($success) {
		$tx->commit;
		say "Added user $name ($email) with UID $user_id";
		say "Temporary password for login: $password";
	}
}

sub delete_user {
	my ( $self, $uid ) = @_;

	my $user_data = $self->app->users->get( uid => $uid );

	if ( not $user_data ) {
		say "UID $uid does not exist.";
		return;
	}

	$self->app->users->flag_deletion( uid => $uid );

	say "User $user_data->{name} (UID $uid) has been flagged for deletion.";
}

sub really_delete_user {
	my ( $self, $uid, $name ) = @_;

	my $user_data = $self->app->users->get( uid => $uid );

	if ( $user_data->{name} ne $name ) {
		say
		  "User name $name does not match UID $uid. Account deletion aborted.";
		return;
	}

	say "Immediate deletion is not implemented yet.";
	return;
}

sub run {
	my ( $self, $command, @args ) = @_;

	if ( $command eq 'add' ) {
		$self->add_user(@args);
	}
	elsif ( $command eq 'delete' ) {
		$self->delete_user(@args);
	}
	elsif ( $command eq 'DELETE' ) {
		$self->really_delete_user(@args);
	}
	else {
		$self->help;
	}
}

1;

__END__

=head1 SYNOPSIS

  Usage: index.pl account add [name] [email]

  Adds user [name] with a temporary password, which is shown on stdout.
  Users can change the password once logged in.

  Usage: index.pl account delete [uid]

  Request deletion of user [uid]. This has the same effect as using the
  account deletion button. The user account and all corresponding data will
  be deleted by a maintenance run after three days.

  Usage: index.pl account DELETE [uid] [name]

  Immediately delete user [uid]/[name] and all associated data. Deletion is
  irrevocable. Deletion is only performed if [name] matches the name of [uid].