]>
git.proxmox.com Git - pve-access-control.git/blob - src/PVE/Auth/Plugin.pm
1 package PVE
::Auth
::Plugin
;
9 use PVE
::Cluster
qw(cfs_register_file cfs_read_file cfs_lock_file);
10 use PVE
::JSONSchema
qw(get_standard_option);
11 use PVE
::SectionConfig
;
14 use base
qw(PVE::SectionConfig);
16 my $domainconfigfile = "domains.cfg";
18 cfs_register_file
($domainconfigfile,
19 sub { __PACKAGE__-
>parse_config(@_); },
20 sub { __PACKAGE__-
>write_config(@_); });
22 sub lock_domain_config
{
23 my ($code, $errmsg) = @_;
25 cfs_lock_file
($domainconfigfile, undef, $code);
28 $errmsg ?
die "$errmsg: $err" : die $err;
32 our $realm_regex = qr/[A-Za-z][A-Za-z0-9\.\-_]+/;
33 our $user_regex = qr![^\s:/]+!;
35 PVE
::JSONSchema
::register_format
('pve-realm', \
&pve_verify_realm
);
36 sub pve_verify_realm
{
37 my ($realm, $noerr) = @_;
39 if ($realm !~ m/^${realm_regex}$/) {
40 return undef if $noerr;
41 die "value does not look like a valid realm\n";
46 PVE
::JSONSchema
::register_standard_option
('realm', {
47 description
=> "Authentication domain ID",
48 type
=> 'string', format
=> 'pve-realm',
52 my $remove_options = "(?:acl|properties|entry)";
54 my $realm_sync_options_desc = {
56 description
=> "Select what to sync.",
58 enum
=> [qw(users groups both)],
61 'remove-vanished' => {
62 description
=> "A semicolon-seperated list of things to remove when they or the user"
63 ." vanishes during a sync. The following values are possible: 'entry' removes the"
64 ." user/group when not returned from the sync. 'properties' removes the set"
65 ." properties on existing user/group that do not appear in the source (even custom ones)."
66 ." 'acl' removes acls when the user/group is not returned from the sync.",
68 typetext
=> "[acl];[properties];[entry]",
69 pattern
=> "(?:$remove_options\;)*$remove_options",
72 # TODO check/rewrite in pve7to8, and remove with 8.0
74 description
=> "DEPRECATED: use 'remove-vanished' instead. If set, uses the LDAP Directory as source of truth,"
75 ." deleting users or groups not returned from the sync and removing"
76 ." all locally modified properties of synced users. If not set,"
77 ." only syncs information which is present in the synced data, and does not"
78 ." delete or modify anything else.",
83 description
=> "Enable newly synced users immediately.",
89 description
=> "DEPRECATED: use 'remove-vanished' instead. Remove ACLs for users or"
90 ." groups which were removed from the config during a sync.",
95 PVE
::JSONSchema
::register_standard_option
('realm-sync-options', $realm_sync_options_desc);
96 PVE
::JSONSchema
::register_format
('realm-sync-options', $realm_sync_options_desc);
98 PVE
::JSONSchema
::register_format
('pve-userid', \
&verify_username
);
100 my ($username, $noerr) = @_;
102 $username = '' if !$username;
103 my $len = length($username);
105 die "user name '$username' is too short\n" if !$noerr;
109 die "user name '$username' is too long ($len > 64)\n" if !$noerr;
113 # we only allow a limited set of characters
114 # colon is not allowed, because we store usernames in
115 # colon separated lists)!
116 # slash is not allowed because it is used as pve API delimiter
117 # also see "man useradd"
118 if ($username =~ m!^(${user_regex})\@(${realm_regex})$!) {
119 return wantarray ?
($username, $1, $2) : $username;
122 die "value '$username' does not look like a valid user name\n" if !$noerr;
127 PVE
::JSONSchema
::register_standard_option
('userid', {
128 description
=> "User ID",
129 type
=> 'string', format
=> 'pve-userid',
135 description
=> "The type of 2nd factor authentication.",
136 format_description
=> 'TFATYPE',
138 enum
=> [qw(yubico oath)],
141 description
=> "Yubico API ID.",
142 format_description
=> 'ID',
147 description
=> "Yubico API Key.",
148 format_description
=> 'KEY',
153 description
=> "Yubico API URL.",
154 format_description
=> 'URL',
159 description
=> "TOTP digits.",
160 format_description
=> 'COUNT',
162 minimum
=> 6, maximum
=> 8,
167 description
=> "TOTP time period.",
168 format_description
=> 'SECONDS',
176 PVE
::JSONSchema
::register_format
('pve-tfa-config', $tfa_format);
178 PVE
::JSONSchema
::register_standard_option
('tfa', {
179 description
=> "Use Two-factor authentication.",
180 type
=> 'string', format
=> 'pve-tfa-config',
185 sub parse_tfa_config
{
188 return PVE
::JSONSchema
::parse_property_string
($tfa_format, $data);
193 type
=> { description
=> "Realm type." },
194 realm
=> get_standard_option
('realm'),
202 sub parse_section_header
{
203 my ($class, $line) = @_;
205 if ($line =~ m/^(\S+):\s*(\S+)\s*$/) {
206 my ($type, $realm) = (lc($1), $2);
207 my $errmsg = undef; # set if you want to skip whole section
208 eval { pve_verify_realm
($realm); };
210 my $config = {}; # to return additional attributes
211 return ($type, $realm, $errmsg, $config);
217 my ($class, $filename, $raw) = @_;
219 my $cfg = $class->SUPER::parse_config
($filename, $raw);
222 foreach my $realm (keys %{$cfg->{ids
}}) {
223 my $data = $cfg->{ids
}->{$realm};
224 # make sure there is only one default marker
225 if ($data->{default}) {
227 delete $data->{default};
233 if ($data->{comment
}) {
234 $data->{comment
} = PVE
::Tools
::decode_text
($data->{comment
});
239 # add default domains
241 $cfg->{ids
}->{pve
}->{type
} = 'pve'; # force type
242 $cfg->{ids
}->{pve
}->{comment
} = "Proxmox VE authentication server"
243 if !$cfg->{ids
}->{pve
}->{comment
};
245 $cfg->{ids
}->{pam
}->{type
} = 'pam'; # force type
246 $cfg->{ids
}->{pam
}->{plugin
} = 'PVE::Auth::PAM';
247 $cfg->{ids
}->{pam
}->{comment
} = "Linux PAM standard authentication"
248 if !$cfg->{ids
}->{pam
}->{comment
};
254 my ($class, $filename, $cfg) = @_;
256 foreach my $realm (keys %{$cfg->{ids
}}) {
257 my $data = $cfg->{ids
}->{$realm};
258 if ($data->{comment
}) {
259 $data->{comment
} = PVE
::Tools
::encode_text
($data->{comment
});
263 $class->SUPER::write_config
($filename, $cfg);
266 sub authenticate_user
{
267 my ($class, $config, $realm, $username, $password) = @_;
273 my ($class, $config, $realm, $username, $password) = @_;
275 my $type = $class->type();
277 die "can't set password on auth type '$type'\n";
281 my ($class, $config, $realm, $username) = @_;
283 # do nothing by default
286 # called during addition of realm (before the new domain config got written)
287 # `password` is moved to %param to avoid writing it out to the config
288 # die to abort addition if there are (grave) problems
289 # NOTE: runs in a domain config *locked* context
291 my ($class, $realm, $config, %param) = @_;
292 # do nothing by default
295 # called during domain configuration update (before the updated domain config got
296 # written). `password` is moved to %param to avoid writing it out to the config
297 # die to abort the update if there are (grave) problems
298 # NOTE: runs in a domain config *locked* context
300 my ($class, $realm, $config, %param) = @_;
301 # do nothing by default
304 # called during deletion of realms (before the new domain config got written)
305 # and if the activate check on addition fails, to cleanup all storage traces
306 # which on_add_hook may have created.
307 # die to abort deletion if there are (very grave) problems
308 # NOTE: runs in a storage config *locked* context
310 my ($class, $realm, $config) = @_;
311 # do nothing by default