]>
git.proxmox.com Git - pmg-api.git/blob - PMG/UserConfig.pm
1 package PMG
::UserConfig
;
11 use PVE
::JSONSchema
qw(get_standard_option);
12 use PVE
::Exception
qw(raise);
16 my $inotify_file_id = 'pmg-user.conf';
17 my $config_filename = '/etc/pmg/user.conf';
22 my $class = ref($type) || $type;
24 my $cfg = PVE
::INotify
::read_file
($inotify_file_id);
26 return bless $cfg, $class;
32 PVE
::INotify
::write_file
($inotify_file_id, $self);
35 my $lockfile = "/var/lock/pmguser.lck";
38 my ($code, $errmsg) = @_;
40 my $p = PVE
::Tools
::lock_file
($lockfile, undef, $code);
42 $errmsg ?
die "$errmsg: $err" : die $err;
47 additionalProperties
=> 0,
49 userid
=> get_standard_option
('userid'),
51 description
=> "Authentication realm.",
53 enum
=> ['pam', 'pmg'],
58 description
=> "Users E-Mail address.",
59 type
=> 'string', format
=> 'email',
63 description
=> "Account expiration date (seconds since epoch). '0' means no expiration date.",
70 description
=> "Flag to enable or disable the account.",
76 description
=> "Encrypted password (see `man crypt`)",
78 pattern
=> '\$\d\$[a-zA-Z0-9\.\/]+\$[a-zA-Z0-9\.\/]+',
82 description
=> "User role. Role 'root' is reseved for the Unix Superuser.",
84 enum
=> ['root', 'admin', 'qmanager', 'audit'],
87 description
=> "First name.",
93 description
=> "Last name.",
99 description
=> "Keys for two factor auth (yubico).",
105 description
=> "Comment.",
112 our $update_schema = clone
($schema);
113 $update_schema->{properties
}->{role}->{optional
} = 1;
114 $update_schema->{properties
}->{delete} = {
115 type
=> 'string', format
=> 'pve-configid-list',
116 description
=> "A list of settings you want to delete.",
121 my $verity_entry = sub {
125 PVE
::JSONSchema
::check_prop
($entry, $schema, '', $errors);
126 if (scalar(%$errors)) {
127 raise
"verify entry failed\n", errors
=> $errors;
131 my $fixup_root_properties = sub {
134 $cfg->{'root@pam'}->{userid
} = 'root@pam';
135 $cfg->{'root@pam'}->{realm
} = 'pam';
136 $cfg->{'root@pam'}->{enable
} = 1;
137 $cfg->{'root@pam'}->{expire
} = 0;
138 $cfg->{'root@pam'}->{comment
} = 'Unix Superuser';
139 $cfg->{'root@pam'}->{role} = 'root';
140 delete $cfg->{'root@pam'}->{crypt_pass
};
144 my ($filename, $fh) = @_;
152 while (defined(my $line = <$fh>)) {
153 next if $line =~ m/^\s*$/;
154 if ($line =~ m/^#(.*)$/) {
160 (?
<userid
>(?
:[^\s
:]+)) :
163 (?
<crypt_pass
>(?
:[^\s
:]*)) :
165 (?
<email
>(?
:[^\s
:]*)) :
166 (?
<firstname
>(?
:[^:]*)) :
167 (?
<lastname
>(?
:[^:]*)) :
172 userid
=> $+{userid
} . '@pmg',
174 enable
=> $+{enable
} || 0,
175 expire
=> $+{expire
} || 0,
178 $d->{comment
} = $comment if $comment;
180 foreach my $k (qw(crypt_pass email firstname lastname keys)) {
181 $d->{$k} = $+{$k} if $+{$k};
185 $cfg->{$d->{userid
}} = $d;
186 die "role 'root' is reserved\n"
187 if $d->{role} eq 'root' && $d->{userid
} ne 'root@pmg';
190 warn "$filename: $err";
193 warn "$filename: ignore invalid line $.\n";
199 # hack: we list root@pam here (root@pmg is an alias for root@pam)
200 $cfg->{'root@pam'} = $cfg->{'root@pmg'} // {};
201 delete $cfg->{'root@pmg'};
202 $cfg->{'root@pam'} //= {};
204 $fixup_root_properties->($cfg);
209 sub write_user_conf
{
210 my ($filename, $fh, $cfg) = @_;
214 $fixup_root_properties->($cfg);
216 foreach my $userid (keys %$cfg) {
217 my $d = $cfg->{$userid};
219 $d->{userid
} = $userid;
221 die "invalid userid '$userid'\n" if $userid eq 'root@pmg';
225 $cfg->{$d->{userid
}} = $d;
227 if ($d->{userid
} ne 'root@pam') {
228 die "role 'root' is reserved\n" if $d->{role} eq 'root';
229 die "unable to add users for realm '$d->{realm}'\n"
230 if $d->{realm
} ne 'pmg';
239 if ($userid eq 'root@pam') {
241 $d->{crypt_pass
} = '',
245 next if $userid !~ m/^(?<username>.+)\@pmg$/;
246 $line = "$+{username}:";
249 for my $k (qw(enable expire crypt_pass role email firstname lastname keys)) {
250 $line .= ($d->{$k} // '') . ':';
252 if (my $comment = $d->{comment
}) {
253 my $firstline = (split /\n/, $comment)[0]; # only allow one line
254 $raw .= "#$firstline\n";
256 $raw .= $line . "\n";
259 PVE
::Tools
::safe_print
($filename, $fh, $raw);
262 PVE
::INotify
::register_file
($inotify_file_id, $config_filename,
266 always_call_parser
=> 1);
268 sub lookup_user_data
{
269 my ($self, $username, $noerr) = @_;
271 return $self->{$username} if $self->{$username};
273 die "no such user ('$username')\n" if !$noerr;
278 sub authenticate_user
{
279 my ($self, $username, $password) = @_;
281 die "no password\n" if !$password;
283 my $data = $self->lookup_user_data($username);
286 my $expire = $data->{expire
};
288 die "account expired\n" if $expire && ($expire < $ctime);
290 if ($data->{crypt_pass
}) {
291 my $encpw = crypt($password, $data->{crypt_pass
});
292 die "invalid credentials\n" if ($encpw ne $data->{crypt_pass
});
294 die "no password set\n";
300 sub set_user_password
{
301 my ($class, $username, $password) = @_;
304 my $cfg = $class->new();
305 my $data = $cfg->lookup_user_data($username); # user exists
306 my $epw = PMG
::Utils
::encrypt_pw
($password);
307 $data->{crypt_pass
} = $epw;