]> git.proxmox.com Git - pmg-api.git/blame - src/PMG/RuleDB/LDAP.pm
ldap: improve unicode support
[pmg-api.git] / src / PMG / RuleDB / LDAP.pm
CommitLineData
10621236
DM
1package PMG::RuleDB::LDAP;
2
3use strict;
4use warnings;
5use DBI;
ff1a61ff 6use Encode qw(encode);
10621236 7
2aeda4ac
DM
8use PVE::Exception qw(raise_param_exc);
9
10621236
DM
10use PMG::Utils;
11use PMG::RuleDB::Object;
12use PMG::LDAPCache;
13use PMG::LDAPSet;
14
15use base qw(PMG::RuleDB::Object);
16
17sub otype {
18 return 1005;
19}
20
21sub oclass {
22 return 'who';
23}
24
25sub otype_text {
26 return 'LDAP Group';
27}
28
10621236
DM
29sub new {
30 my ($type, $ldapgroup, $profile, $ogroup) = @_;
31
32 my $class = ref($type) || $type;
33
34 my $self = $class->SUPER::new($class->otype(), $ogroup);
35
36 $self->{ldapgroup} = $ldapgroup // '';
37 $self->{profile} = $profile // '';
38
39 return $self;
40}
41
42sub load_attr {
43 my ($type, $ruledb, $id, $ogroup, $value) = @_;
44
45 my $class = ref($type) || $type;
46
47 defined($value) || die "undefined value: ERROR";
48
ff1a61ff
DC
49 my $decoded = PMG::Utils::try_decode_utf8($value);
50
10621236 51 my $obj;
ff1a61ff 52 if ($decoded =~ m/^([^:]*):(.*)$/) {
10621236 53 $obj = $class->new($2, $1, $ogroup);
ff1a61ff 54 $obj->{digest} = Digest::SHA::sha1_hex($id, encode('UTF-8', $2), encode('UTF-8', $1), $ogroup);
10621236 55 } else {
ff1a61ff 56 $obj = $class->new($decoded, '', $ogroup);
2aeda4ac 57 $obj->{digest} = Digest::SHA::sha1_hex($id, $value, '#', $ogroup);
10621236
DM
58 }
59
60 $obj->{id} = $id;
61
62 return $obj;
63}
64
65sub save {
66 my ($self, $ruledb) = @_;
67
68 defined($self->{ogroup}) || die "undefined ogroup: ERROR";
69 defined($self->{ldapgroup}) || die "undefined ldap group: ERROR";
70 defined($self->{profile}) || die "undefined ldap profile: ERROR";
71
72 my $grp = $self->{ldapgroup};
73 my $profile = $self->{profile};
74
ff1a61ff 75 my $confdata = encode('UTF-8', "$profile:$grp");
10621236
DM
76
77 if (defined ($self->{id})) {
78 # update
79
80 $ruledb->{dbh}->do(
81 "UPDATE Object SET Value = ? WHERE ID = ?",
82 undef, $confdata, $self->{id});
83
84 } else {
85 # insert
86
736b986f
DC
87 # check if it exists first
88 if (my $id = PMG::Utils::get_existing_object_id(
89 $ruledb->{dbh},
90 $self->{ogroup},
91 $self->otype(),
92 $confdata
93 )) {
94 return $id;
95 }
96
10621236
DM
97 my $sth = $ruledb->{dbh}->prepare(
98 "INSERT INTO Object (Objectgroup_ID, ObjectType, Value) " .
99 "VALUES (?, ?, ?);");
100
101 $sth->execute($self->{ogroup}, $self->otype, $confdata);
102
103 $self->{id} = PMG::Utils::lastid($ruledb->{dbh}, 'object_id_seq');
104 }
105
106 return $self->{id};
107}
108
109sub test_ldap {
110 my ($ldap, $addr, $group, $profile) = @_;
111
112 if ($group eq '') {
113 return $ldap->mail_exists($addr, $profile);
114 } elsif ($group eq '-') {
115 return !$ldap->mail_exists($addr, $profile);
2aeda4ac 116 } elsif ($profile) {
10621236 117 return $ldap->user_in_group ($addr, $group, $profile);
2aeda4ac
DM
118 } else {
119 # fail if we have a real $group without $profile
120 return 0;
10621236
DM
121 }
122}
123
124sub who_match {
125 my ($self, $addr, $ip, $ldap) = @_;
126
127 return 0 if !$ldap;
128
129 return test_ldap($ldap, $addr, $self->{ldapgroup}, $self->{profile});
130}
131
2aeda4ac
DM
132sub short_desc {
133 my ($self) = @_;
134
135 my $desc;
136
137 my $profile = $self->{profile};
138 my $group = $self->{ldapgroup};
139
140 if ($group eq '') {
141 $desc = "Existing LDAP address";
687306ad
DM
142 if ($profile) {
143 $desc .= ", profile '$profile'";
144 } else {
145 $desc .= ", any profile";
146 }
2aeda4ac
DM
147 } elsif ($group eq '-') {
148 $desc = "Unknown LDAP address";
687306ad
DM
149 if ($profile) {
150 $desc .= ", profile '$profile'";
151 } else {
152 $desc .= ", any profile";
153 }
2aeda4ac 154 } elsif ($profile) {
f76f331a 155 $desc = "LDAP group '$group', profile '$profile'";
2aeda4ac
DM
156 } else {
157 $desc = "LDAP group without profile - fail always";
158 }
159
160 return $desc;
161}
162
163sub properties {
164 my ($class) = @_;
165
166 return {
167 mode => {
168 description => "Operational mode. You can either match 'any' user, match when no such user exists with 'none', or match when the user is member of a specific group.",
169 type => 'string',
170 enum => ['any', 'none', 'group'],
171 },
172 profile => {
173 description => "Profile ID.",
174 type => 'string', format => 'pve-configid',
175 optional => 1,
176 },
177 group => {
178 description => "LDAP Group DN",
179 type => 'string',
180 maxLength => 1024,
181 minLength => 1,
182 optional => 1,
183 },
184 };
185}
186
187sub get {
188 my ($self) = @_;
189
190 my $group = $self->{ldapgroup};
191 my $profile = $self->{profile},
192
193 my $data = {};
194
195 if ($group eq '') {
196 $data->{mode} = 'any';
197 } elsif ($group eq '-') {
198 $data->{mode} = 'none';
199 } else {
200 $data->{mode} = 'group';
201 $data->{group} = $group;
202 }
203
204 $data->{profile} = $profile if $profile ne '';
205
206 return $data;
207 }
208
209sub update {
210 my ($self, $param) = @_;
211
212 my $mode = $param->{mode};
213
d974ca19
DM
214 if (defined(my $profile = $param->{profile})) {
215 my $cfg = PVE::INotify::read_file("pmg-ldap.conf");
216 my $config = $cfg->{ids}->{$profile};
217 die "LDAP profile '$profile' does not exist\n" if !$config;
218
219 if (defined(my $group = $param->{group})) {
220 my $ldapcache = PMG::LDAPCache->new(
221 id => $profile, syncmode => 1, %$config);
222
223 die "LDAP group '$group' does not exist\n"
224 if !$ldapcache->group_exists($group);
225 }
226 }
227
2aeda4ac 228 if ($mode eq 'any') {
5182cea0 229 raise_param_exc({ group => "parameter not allwed with mode '$mode'"})
2aeda4ac
DM
230 if defined($param->{group});
231 $self->{ldapgroup} = '';
232 $self->{profile} = $param->{profile} // '';
233 } elsif ($mode eq 'none') {
5182cea0 234 raise_param_exc({ group => "parameter not allwed with mode '$mode'"})
2aeda4ac
DM
235 if defined($param->{group});
236 $self->{ldapgroup} = '-';
237 $self->{profile} = $param->{profile} // '';
238 } elsif ($mode eq 'group') {
5182cea0 239 raise_param_exc({ group => "parameter is required with mode '$mode'"})
2aeda4ac
DM
240 if !defined($param->{group});
241 $self->{ldapgroup} = $param->{group};
5182cea0 242 raise_param_exc({ profile => "parameter is required with mode '$mode'"})
2aeda4ac
DM
243 if !defined($param->{profile});
244 $self->{profile} = $param->{profile};
245 } else {
246 die "internal error"; # just to me sure
247 }
248}
249
10621236
DM
2501;
251
252__END__
253
254=head1 PMG::RuleDB::LDAP
255
256A WHO object to check LDAP groups
257
1359baef 258=head2 Attributes
10621236
DM
259
260=head3 ldapgroup
261
262An LDAP group (ignore case).
263
264=head3 profile
265
266The LDAP profile name
267
268=head2 Examples
269
270 $obj = PMG::RuleDB::LDAP>new ('groupname', 'profile_name');