]>
Commit | Line | Data |
---|---|---|
a6e3ac60 DM |
1 | package PMG::LDAPConfig; |
2 | ||
3 | use strict; | |
4 | use warnings; | |
49a16f65 | 5 | use MIME::Base64; |
a6e3ac60 DM |
6 | use Data::Dumper; |
7 | ||
8 | use PVE::Tools; | |
9 | use PVE::JSONSchema qw(get_standard_option); | |
10 | use PVE::INotify; | |
11 | use PVE::SectionConfig; | |
12 | ||
13 | use base qw(PVE::SectionConfig); | |
14 | ||
ca0c8b88 DM |
15 | PVE::JSONSchema::register_format('ldap-simple-attr', \&verify_ldap_simple_attr); |
16 | sub verify_ldap_simple_attr { | |
17 | my ($attr, $noerr) = @_; | |
18 | ||
19 | if ($attr =~ m/^[a-zA-Z0-9]+$/) { | |
20 | return $attr; | |
21 | } | |
22 | ||
23 | die "value '$attr' does not look like a simple ldap attribute name\n" if !$noerr; | |
24 | ||
25 | return undef; | |
26 | } | |
27 | ||
7d90f962 DM |
28 | my $inotify_file_id = 'pmg-ldap.conf'; |
29 | my $config_filename = '/etc/pmg/ldap.conf'; | |
30 | ||
a6e3ac60 DM |
31 | my $defaultData = { |
32 | propertyList => { | |
33 | type => { description => "Section type." }, | |
c2ef4490 | 34 | profile => { |
2aeda4ac | 35 | description => "Profile ID.", |
a6e3ac60 DM |
36 | type => 'string', format => 'pve-configid', |
37 | }, | |
2fdba966 DM |
38 | }, |
39 | }; | |
40 | ||
ca0c8b88 | 41 | |
2fdba966 DM |
42 | sub properties { |
43 | return { | |
1c4fa5b1 DM |
44 | disable => { |
45 | description => "Flag to disable/deactivate the entry.", | |
46 | type => 'boolean', | |
47 | optional => 1, | |
48 | }, | |
bfed5777 DM |
49 | comment => { |
50 | description => "Description.", | |
51 | type => 'string', | |
52 | optional => 1, | |
53 | maxLength => 4096, | |
54 | }, | |
a6e3ac60 DM |
55 | mode => { |
56 | description => "LDAP protocol mode ('ldap' or 'ldaps').", | |
57 | type => 'string', | |
58 | enum => ['ldap', 'ldaps'], | |
59 | default => 'ldap', | |
60 | }, | |
49a16f65 DM |
61 | server1 => { |
62 | description => "Server address.", | |
63 | type => 'string', format => 'address', | |
bfed5777 | 64 | maxLength => 256, |
49a16f65 DM |
65 | }, |
66 | server2 => { | |
67 | description => "Fallback server address. Userd when the first server is not available.", | |
68 | type => 'string', format => 'address', | |
bfed5777 | 69 | maxLength => 256, |
49a16f65 DM |
70 | }, |
71 | port => { | |
72 | description => "Specify the port to connect to.", | |
73 | type => 'integer', | |
74 | minimum => 1, | |
75 | maximum => 65535, | |
76 | }, | |
77 | binddn => { | |
78 | description => "Bind domain name.", | |
79 | type => 'string', | |
80 | }, | |
81 | bindpw => { | |
82 | description => "Bind password.", | |
83 | type => 'string', | |
84 | }, | |
85 | basedn => { | |
86 | description => "Base domain name.", | |
87 | type => 'string', | |
88 | }, | |
89 | groupbasedn => { | |
90 | description => "Base domain name for groups.", | |
91 | type => 'string', | |
92 | }, | |
93 | filter => { | |
94 | description => "LDAP filter.", | |
95 | type => 'string', | |
96 | }, | |
97 | accountattr => { | |
98 | description => "Account attribute name name.", | |
ca0c8b88 | 99 | type => 'string', format => 'ldap-simple-attr-list', |
23b1d0f8 | 100 | default => 'sAMAccountName, uid', |
49a16f65 DM |
101 | }, |
102 | mailattr => { | |
103 | description => "List of mail attribute names.", | |
ca0c8b88 | 104 | type => 'string', format => 'ldap-simple-attr-list', |
7fc9e381 | 105 | default => "mail, userPrincipalName, proxyAddresses, othermailbox, mailAlternativeAddress", |
49a16f65 | 106 | }, |
b14970ad DC |
107 | groupclass => { |
108 | description => "List of objectclasses for groups.", | |
ca0c8b88 | 109 | type => 'string', format => 'ldap-simple-attr-list', |
b14970ad DC |
110 | default => "group, univentionGroup, ipausergroup", |
111 | }, | |
2fdba966 DM |
112 | }; |
113 | } | |
a6e3ac60 DM |
114 | |
115 | sub options { | |
116 | return { | |
ff4776b6 | 117 | disable => { optional => 1 }, |
bfed5777 | 118 | comment => { optional => 1 }, |
49a16f65 DM |
119 | server1 => { optional => 0 }, |
120 | server2 => { optional => 1 }, | |
121 | port => { optional => 1 }, | |
a6e3ac60 | 122 | mode => { optional => 1 }, |
49a16f65 DM |
123 | binddn => { optional => 1 }, |
124 | bindpw => { optional => 1 }, | |
125 | basedn => { optional => 1 }, | |
126 | groupbasedn => { optional => 1 }, | |
127 | filter => { optional => 1 }, | |
128 | accountattr => { optional => 1 }, | |
129 | mailattr => { optional => 1 }, | |
b14970ad | 130 | groupclass => { optional => 1 }, |
a6e3ac60 DM |
131 | }; |
132 | } | |
133 | ||
134 | sub type { | |
135 | return 'ldap'; | |
136 | } | |
137 | ||
138 | sub private { | |
139 | return $defaultData; | |
140 | } | |
141 | ||
05b856e3 DM |
142 | sub parse_section_header { |
143 | my ($class, $line) = @_; | |
144 | ||
145 | if ($line =~ m/^(\S+):\s*(\S+)\s*$/) { | |
c2ef4490 | 146 | my ($type, $profileId) = ($1, $2); |
05b856e3 | 147 | my $errmsg = undef; # set if you want to skip whole section |
c2ef4490 | 148 | eval { PVE::JSONSchema::pve_verify_configid($profileId); }; |
05b856e3 DM |
149 | $errmsg = $@ if $@; |
150 | my $config = {}; # to return additional attributes | |
c2ef4490 | 151 | return ($type, $profileId, $errmsg, $config); |
05b856e3 DM |
152 | } |
153 | return undef; | |
154 | } | |
155 | ||
bfed5777 DM |
156 | sub parse_config { |
157 | my ($class, $filename, $raw) = @_; | |
158 | ||
159 | my $cfg = $class->SUPER::parse_config($filename, $raw); | |
160 | ||
c2ef4490 DM |
161 | foreach my $profile (keys %{$cfg->{ids}}) { |
162 | my $data = $cfg->{ids}->{$profile}; | |
bfed5777 DM |
163 | |
164 | $data->{comment} = PVE::Tools::decode_text($data->{comment}) | |
165 | if defined($data->{comment}); | |
166 | ||
167 | $data->{bindpw} = decode_base64($data->{bindpw}) | |
168 | if defined($data->{bindpw}); | |
169 | } | |
170 | ||
171 | return $cfg; | |
172 | } | |
173 | ||
174 | sub write_config { | |
175 | my ($class, $filename, $cfg) = @_; | |
176 | ||
c2ef4490 DM |
177 | foreach my $profile (keys %{$cfg->{ids}}) { |
178 | my $data = $cfg->{ids}->{$profile}; | |
bfed5777 DM |
179 | |
180 | $data->{comment} = PVE::Tools::encode_text($data->{comment}) | |
181 | if defined($data->{comment}); | |
182 | ||
183 | $data->{bindpw} = encode_base64($data->{bindpw}, '') | |
184 | if defined($data->{bindpw}); | |
185 | } | |
186 | ||
187 | $class->SUPER::write_config($filename, $cfg); | |
188 | } | |
189 | ||
7d90f962 DM |
190 | sub new { |
191 | my ($type) = @_; | |
192 | ||
193 | my $class = ref($type) || $type; | |
194 | ||
195 | my $cfg = PVE::INotify::read_file($inotify_file_id); | |
196 | ||
197 | return bless $cfg, $class; | |
198 | } | |
199 | ||
200 | sub write { | |
201 | my ($self) = @_; | |
202 | ||
203 | PVE::INotify::write_file($inotify_file_id, $self); | |
204 | } | |
205 | ||
e1c64277 DM |
206 | my $lockfile = "/var/lock/pmgldapconfig.lck"; |
207 | ||
208 | sub lock_config { | |
209 | my ($code, $errmsg) = @_; | |
210 | ||
211 | my $p = PVE::Tools::lock_file($lockfile, undef, $code); | |
212 | if (my $err = $@) { | |
213 | $errmsg ? die "$errmsg: $err" : die $err; | |
214 | } | |
215 | } | |
216 | ||
49a16f65 | 217 | |
a6e3ac60 DM |
218 | __PACKAGE__->register(); |
219 | __PACKAGE__->init(); | |
220 | ||
221 | sub read_pmg_ldap_conf { | |
222 | my ($filename, $fh) = @_; | |
223 | ||
224 | local $/ = undef; # slurp mode | |
225 | ||
bdbc2bc5 | 226 | my $raw = defined($fh) ? <$fh> : ''; |
a6e3ac60 DM |
227 | |
228 | return __PACKAGE__->parse_config($filename, $raw); | |
229 | } | |
230 | ||
231 | sub write_pmg_ldap_conf { | |
232 | my ($filename, $fh, $cfg) = @_; | |
233 | ||
234 | my $raw = __PACKAGE__->write_config($filename, $cfg); | |
235 | ||
bdbc2bc5 DM |
236 | my $gid = getgrnam('www-data'); |
237 | chown(0, $gid, $fh); | |
238 | chmod(0640, $fh); | |
d5121ced | 239 | |
a6e3ac60 DM |
240 | PVE::Tools::safe_print($filename, $fh, $raw); |
241 | } | |
242 | ||
7d90f962 | 243 | PVE::INotify::register_file($inotify_file_id, $config_filename, |
a6e3ac60 | 244 | \&read_pmg_ldap_conf, |
bdbc2bc5 DM |
245 | \&write_pmg_ldap_conf, |
246 | undef, | |
247 | always_call_parser => 1); | |
a6e3ac60 DM |
248 | |
249 | ||
250 | 1; |