]> git.proxmox.com Git - pve-access-control.git/blame - PVE/Auth/Plugin.pm
realm: add default-sync-options to config
[pve-access-control.git] / PVE / Auth / Plugin.pm
CommitLineData
5bb4e06a
DM
1package PVE::Auth::Plugin;
2
3use strict;
4use warnings;
5use Encode;
6use Digest::SHA;
7use PVE::Tools;
8use PVE::SectionConfig;
9use PVE::JSONSchema qw(get_standard_option);
10use PVE::Cluster qw(cfs_register_file cfs_read_file cfs_lock_file);
11
5bb4e06a
DM
12use base qw(PVE::SectionConfig);
13
14my $domainconfigfile = "domains.cfg";
15
0a6e09fd 16cfs_register_file($domainconfigfile,
5bb4e06a
DM
17 sub { __PACKAGE__->parse_config(@_); },
18 sub { __PACKAGE__->write_config(@_); });
19
20sub lock_domain_config {
21 my ($code, $errmsg) = @_;
22
23 cfs_lock_file($domainconfigfile, undef, $code);
24 my $err = $@;
25 if ($err) {
26 $errmsg ? die "$errmsg: $err" : die $err;
27 }
28}
29
8e23f971
FG
30our $realm_regex = qr/[A-Za-z][A-Za-z0-9\.\-_]+/;
31our $user_regex = qr![^\s:/]+!;
5bb4e06a
DM
32
33PVE::JSONSchema::register_format('pve-realm', \&pve_verify_realm);
34sub pve_verify_realm {
35 my ($realm, $noerr) = @_;
0a6e09fd 36
5bb4e06a
DM
37 if ($realm !~ m/^${realm_regex}$/) {
38 return undef if $noerr;
0a6e09fd 39 die "value does not look like a valid realm\n";
5bb4e06a
DM
40 }
41 return $realm;
42}
43
44PVE::JSONSchema::register_standard_option('realm', {
45 description => "Authentication domain ID",
46 type => 'string', format => 'pve-realm',
47 maxLength => 32,
48});
49
d29d2d4a
TL
50my $realm_sync_options_desc = {
51 scope => {
52 description => "Select what to sync.",
53 type => 'string',
54 enum => [qw(users groups both)],
55 optional => '1',
56 },
57 full => {
58 description => "If set, uses the LDAP Directory as source of truth,"
59 ." deleting users or groups not returned from the sync. Otherwise"
60 ." only syncs information which is not already present, and does not"
61 ." deletes or modifies anything else.",
62 type => 'boolean',
63 optional => '1',
64 },
65 'enable-new' => {
66 description => "Enable newly synced users immediately.",
67 type => 'boolean',
68 default => '1',
69 optional => '1',
70 },
71 purge => {
72 description => "Remove ACLs for users or groups which were removed from"
73 ." the config during a sync.",
74 type => 'boolean',
75 optional => '1',
76 },
77};
78PVE::JSONSchema::register_standard_option('realm-sync-options', $realm_sync_options_desc);
79PVE::JSONSchema::register_format('realm-sync-options', $realm_sync_options_desc);
80
5bb4e06a
DM
81PVE::JSONSchema::register_format('pve-userid', \&verify_username);
82sub verify_username {
83 my ($username, $noerr) = @_;
84
85 $username = '' if !$username;
86 my $len = length($username);
87 if ($len < 3) {
88 die "user name '$username' is too short\n" if !$noerr;
89 return undef;
90 }
91 if ($len > 64) {
92 die "user name '$username' is too long ($len > 64)\n" if !$noerr;
93 return undef;
94 }
95
96 # we only allow a limited set of characters
0a6e09fd 97 # colon is not allowed, because we store usernames in
5bb4e06a
DM
98 # colon separated lists)!
99 # slash is not allowed because it is used as pve API delimiter
0a6e09fd 100 # also see "man useradd"
8e23f971 101 if ($username =~ m!^(${user_regex})\@(${realm_regex})$!) {
5bb4e06a
DM
102 return wantarray ? ($username, $1, $2) : $username;
103 }
104
105 die "value '$username' does not look like a valid user name\n" if !$noerr;
106
107 return undef;
108}
109
110PVE::JSONSchema::register_standard_option('userid', {
af5d7da7 111 description => "User ID",
5bb4e06a
DM
112 type => 'string', format => 'pve-userid',
113 maxLength => 64,
114});
115
9401be39
WB
116my $tfa_format = {
117 type => {
118 description => "The type of 2nd factor authentication.",
119 format_description => 'TFATYPE',
120 type => 'string',
121 enum => [qw(yubico oath)],
122 },
123 id => {
124 description => "Yubico API ID.",
125 format_description => 'ID',
126 type => 'string',
127 optional => 1,
128 },
129 key => {
130 description => "Yubico API Key.",
131 format_description => 'KEY',
132 type => 'string',
133 optional => 1,
134 },
135 url => {
136 description => "Yubico API URL.",
137 format_description => 'URL',
138 type => 'string',
139 optional => 1,
140 },
141 digits => {
142 description => "TOTP digits.",
143 format_description => 'COUNT',
144 type => 'integer',
145 minimum => 6, maximum => 8,
146 default => 6,
147 optional => 1,
148 },
149 step => {
150 description => "TOTP time period.",
151 format_description => 'SECONDS',
152 type => 'integer',
153 minimum => 10,
154 default => 30,
155 optional => 1,
156 },
157};
158
159PVE::JSONSchema::register_format('pve-tfa-config', $tfa_format);
96f8ebd6
DM
160
161PVE::JSONSchema::register_standard_option('tfa', {
162 description => "Use Two-factor authentication.",
163 type => 'string', format => 'pve-tfa-config',
164 optional => 1,
165 maxLength => 128,
166});
167
168sub parse_tfa_config {
169 my ($data) = @_;
170
9401be39 171 return PVE::JSONSchema::parse_property_string($tfa_format, $data);
96f8ebd6
DM
172}
173
5bb4e06a
DM
174my $defaultData = {
175 propertyList => {
176 type => { description => "Realm type." },
177 realm => get_standard_option('realm'),
178 },
179};
180
181sub private {
182 return $defaultData;
183}
184
185sub parse_section_header {
186 my ($class, $line) = @_;
187
188 if ($line =~ m/^(\S+):\s*(\S+)\s*$/) {
189 my ($type, $realm) = (lc($1), $2);
190 my $errmsg = undef; # set if you want to skip whole section
191 eval { pve_verify_realm($realm); };
192 $errmsg = $@ if $@;
193 my $config = {}; # to return additional attributes
194 return ($type, $realm, $errmsg, $config);
195 }
196 return undef;
197}
198
199sub parse_config {
200 my ($class, $filename, $raw) = @_;
201
202 my $cfg = $class->SUPER::parse_config($filename, $raw);
203
204 my $default;
205 foreach my $realm (keys %{$cfg->{ids}}) {
206 my $data = $cfg->{ids}->{$realm};
207 # make sure there is only one default marker
208 if ($data->{default}) {
209 if ($default) {
210 delete $data->{default};
211 } else {
212 $default = $realm;
213 }
214 }
215
216 if ($data->{comment}) {
217 $data->{comment} = PVE::Tools::decode_text($data->{comment});
218 }
219
220 }
221
222 # add default domains
223
96f8ebd6
DM
224 $cfg->{ids}->{pve}->{type} = 'pve'; # force type
225 $cfg->{ids}->{pve}->{comment} = "Proxmox VE authentication server"
226 if !$cfg->{ids}->{pve}->{comment};
5bb4e06a 227
96f8ebd6
DM
228 $cfg->{ids}->{pam}->{type} = 'pam'; # force type
229 $cfg->{ids}->{pam}->{plugin} = 'PVE::Auth::PAM';
230 $cfg->{ids}->{pam}->{comment} = "Linux PAM standard authentication"
231 if !$cfg->{ids}->{pam}->{comment};
5bb4e06a
DM
232
233 return $cfg;
234};
235
236sub write_config {
237 my ($class, $filename, $cfg) = @_;
238
5bb4e06a
DM
239 foreach my $realm (keys %{$cfg->{ids}}) {
240 my $data = $cfg->{ids}->{$realm};
241 if ($data->{comment}) {
242 $data->{comment} = PVE::Tools::encode_text($data->{comment});
243 }
244 }
0a6e09fd 245
5bb4e06a
DM
246 $class->SUPER::write_config($filename, $cfg);
247}
248
249sub authenticate_user {
250 my ($class, $config, $realm, $username, $password) = @_;
251
252 die "overwrite me";
253}
254
255sub store_password {
256 my ($class, $config, $realm, $username, $password) = @_;
257
258 my $type = $class->type();
259
260 die "can't set password on auth type '$type'\n";
261}
262
263sub delete_user {
264 my ($class, $config, $realm, $username) = @_;
265
266 # do nothing by default
267}
268
2691;