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