#! /usr/bin/env perl use strict; use Hydra::Schema; use Hydra::Helper::Nix; use Hydra::Model::DB; use Getopt::Long qw(:config gnu_getopt); sub showHelp { print q% Usage: hydra-create-user NAME [--rename-from NAME] [--type hydra|google|github] [--full-name FULLNAME] [--email-address EMAIL-ADDRESS] [--password PASSWORD] [--password-hash HASH] [--wipe-roles] [--role ROLE]... Create a new Hydra user account, or update or an existing one. The --role flag can be given multiple times. If the account already exists, roles are added to the existing roles unless --wipe-roles is specified. If --rename-from is given, the specified account is renamed. * PASSWORD HASH The password hash should be an Argon2id hash, which can be generated via: $ nix-shell -p libargon2 [nix-shell]$ argon2 "$(LC_ALL=C tr -dc '[:alnum:]' < /dev/urandom | head -c16)" -id -t 3 -k 262144 -p 1 -l 16 -e foobar Ctrl^D $argon2id$v=19$m=262144,t=3,p=1$NFU1QXJRNnc4V1BhQ0NJQg$6GHqjqv5cNDDwZqrqUD0zQ SHA1 is also accepted, but SHA1 support is deprecated and the user's password will be upgraded to Argon2id on first login. Examples: Create a user with an argon2 password: $ hydra-create-user alice --password-hash '$argon2id$v=19$m=262144,t=3,p=1$NFU1QXJRNnc4V1BhQ0NJQg$6GHqjqv5cNDDwZqrqUD0zQ' --role admin Create a user with a password insecurely provided on the commandline: $ hydra-create-user alice --password foobar --role admin %; exit 0; } my ($renameFrom, $type, $fullName, $emailAddress, $password, $passwordHash); my $wipeRoles = 0; my @roles; GetOptions("rename-from=s" => \$renameFrom, "type=s" => \$type, "full-name=s" => \$fullName, "email-address=s" => \$emailAddress, "password=s" => \$password, "password-hash=s" => \$passwordHash, "wipe-roles" => \$wipeRoles, "role=s" => \@roles, "help" => sub { showHelp() } ) or exit 1; die "$0: one user name required\n" if scalar @ARGV != 1; my $userName = $ARGV[0]; die "$0: type must be `hydra', `google' or `github'\n" if defined $type && $type ne "hydra" && $type ne "google" && $type ne "github"; my $db = Hydra::Model::DB->new(); $db->txn_do(sub { my $user = $db->resultset('Users')->find({ username => $renameFrom // $userName }); if ($renameFrom) { die "$0: user `$renameFrom' does not exist\n" unless $user; $user->update({ username => $userName }); } elsif ($user) { print STDERR "updating existing user `$userName'\n"; } else { print STDERR "creating new user `$userName'\n"; $user = $db->resultset('Users')->create( { username => $userName, type => "hydra", emailaddress => "", password => "!" }); } die "$0: Google or GitHub user names must be email addresses\n" if ($user->type eq "google" || $user->type eq "github") && $userName !~ /\@/; $user->update({ type => $type }) if defined $type; $user->update({ fullname => $fullName eq "" ? undef : $fullName }) if defined $fullName; if ($user->type eq "google" || $user->type eq "github") { die "$0: Google and GitHub accounts do not have an explicitly set email address.\n" if defined $emailAddress; die "$0: Google and GitHub accounts do not have a password.\n" if defined $password; die "$0: Google and GitHub accounts do not have a password.\n" if defined $passwordHash; $user->update({ emailaddress => $userName, password => "!" }); } else { $user->update({ emailaddress => $emailAddress }) if defined $emailAddress; if (defined $password && !(defined $passwordHash)) { $user->setPassword($password); } $user->update({ password => $passwordHash }) if defined $passwordHash; } $user->userroles->delete if $wipeRoles; $user->userroles->update_or_create({ role => $_ }) foreach @roles; });