]>
git.proxmox.com Git - pve-access-control.git/blob - src/PVE/Auth/Plugin.pm
bae9fb9e69bcde0cfdbe2d31657414a05f0e0add
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."
67 ." Instead of a list it also can be 'none' (the default).",
70 typetext
=> "([acl];[properties];[entry])|none",
71 pattern
=> "(?:(?:$remove_options\;)*$remove_options)|none",
74 # TODO check/rewrite in pve7to8, and remove with 8.0
76 description
=> "DEPRECATED: use 'remove-vanished' instead. If set, uses the LDAP Directory as source of truth,"
77 ." deleting users or groups not returned from the sync and removing"
78 ." all locally modified properties of synced users. If not set,"
79 ." only syncs information which is present in the synced data, and does not"
80 ." delete or modify anything else.",
85 description
=> "Enable newly synced users immediately.",
91 description
=> "DEPRECATED: use 'remove-vanished' instead. Remove ACLs for users or"
92 ." groups which were removed from the config during a sync.",
97 PVE
::JSONSchema
::register_standard_option
('realm-sync-options', $realm_sync_options_desc);
98 PVE
::JSONSchema
::register_format
('realm-sync-options', $realm_sync_options_desc);
100 PVE
::JSONSchema
::register_format
('pve-userid', \
&verify_username
);
101 sub verify_username
{
102 my ($username, $noerr) = @_;
104 $username = '' if !$username;
105 my $len = length($username);
107 die "user name '$username' is too short\n" if !$noerr;
111 die "user name '$username' is too long ($len > 64)\n" if !$noerr;
115 # we only allow a limited set of characters
116 # colon is not allowed, because we store usernames in
117 # colon separated lists)!
118 # slash is not allowed because it is used as pve API delimiter
119 # also see "man useradd"
120 if ($username =~ m!^(${user_regex})\@(${realm_regex})$!) {
121 return wantarray ?
($username, $1, $2) : $username;
124 die "value '$username' does not look like a valid user name\n" if !$noerr;
129 PVE
::JSONSchema
::register_standard_option
('userid', {
130 description
=> "User ID",
131 type
=> 'string', format
=> 'pve-userid',
137 description
=> "The type of 2nd factor authentication.",
138 format_description
=> 'TFATYPE',
140 enum
=> [qw(yubico oath)],
143 description
=> "Yubico API ID.",
144 format_description
=> 'ID',
149 description
=> "Yubico API Key.",
150 format_description
=> 'KEY',
155 description
=> "Yubico API URL.",
156 format_description
=> 'URL',
161 description
=> "TOTP digits.",
162 format_description
=> 'COUNT',
164 minimum
=> 6, maximum
=> 8,
169 description
=> "TOTP time period.",
170 format_description
=> 'SECONDS',
178 PVE
::JSONSchema
::register_format
('pve-tfa-config', $tfa_format);
180 PVE
::JSONSchema
::register_standard_option
('tfa', {
181 description
=> "Use Two-factor authentication.",
182 type
=> 'string', format
=> 'pve-tfa-config',
187 sub parse_tfa_config
{
190 return PVE
::JSONSchema
::parse_property_string
($tfa_format, $data);
195 type
=> { description
=> "Realm type." },
196 realm
=> get_standard_option
('realm'),
204 sub parse_section_header
{
205 my ($class, $line) = @_;
207 if ($line =~ m/^(\S+):\s*(\S+)\s*$/) {
208 my ($type, $realm) = (lc($1), $2);
209 my $errmsg = undef; # set if you want to skip whole section
210 eval { pve_verify_realm
($realm); };
212 my $config = {}; # to return additional attributes
213 return ($type, $realm, $errmsg, $config);
219 my ($class, $filename, $raw) = @_;
221 my $cfg = $class->SUPER::parse_config
($filename, $raw);
224 foreach my $realm (keys %{$cfg->{ids
}}) {
225 my $data = $cfg->{ids
}->{$realm};
226 # make sure there is only one default marker
227 if ($data->{default}) {
229 delete $data->{default};
235 if ($data->{comment
}) {
236 $data->{comment
} = PVE
::Tools
::decode_text
($data->{comment
});
241 # add default domains
243 $cfg->{ids
}->{pve
}->{type
} = 'pve'; # force type
244 $cfg->{ids
}->{pve
}->{comment
} = "Proxmox VE authentication server"
245 if !$cfg->{ids
}->{pve
}->{comment
};
247 $cfg->{ids
}->{pam
}->{type
} = 'pam'; # force type
248 $cfg->{ids
}->{pam
}->{plugin
} = 'PVE::Auth::PAM';
249 $cfg->{ids
}->{pam
}->{comment
} = "Linux PAM standard authentication"
250 if !$cfg->{ids
}->{pam
}->{comment
};
256 my ($class, $filename, $cfg) = @_;
258 foreach my $realm (keys %{$cfg->{ids
}}) {
259 my $data = $cfg->{ids
}->{$realm};
260 if ($data->{comment
}) {
261 $data->{comment
} = PVE
::Tools
::encode_text
($data->{comment
});
265 $class->SUPER::write_config
($filename, $cfg);
268 sub authenticate_user
{
269 my ($class, $config, $realm, $username, $password) = @_;
275 my ($class, $config, $realm, $username, $password) = @_;
277 my $type = $class->type();
279 die "can't set password on auth type '$type'\n";
283 my ($class, $config, $realm, $username) = @_;
285 # do nothing by default
288 # called during addition of realm (before the new domain config got written)
289 # `password` is moved to %param to avoid writing it out to the config
290 # die to abort addition if there are (grave) problems
291 # NOTE: runs in a domain config *locked* context
293 my ($class, $realm, $config, %param) = @_;
294 # do nothing by default
297 # called during domain configuration update (before the updated domain config got
298 # written). `password` is moved to %param to avoid writing it out to the config
299 # die to abort the update if there are (grave) problems
300 # NOTE: runs in a domain config *locked* context
302 my ($class, $realm, $config, %param) = @_;
303 # do nothing by default
306 # called during deletion of realms (before the new domain config got written)
307 # and if the activate check on addition fails, to cleanup all storage traces
308 # which on_add_hook may have created.
309 # die to abort deletion if there are (very grave) problems
310 # NOTE: runs in a storage config *locked* context
312 my ($class, $realm, $config) = @_;
313 # do nothing by default