1 #include "config.h"
2 #include "phosphor-ldap-config/ldap_config.hpp"
3 #include "phosphor-ldap-config/ldap_config_mgr.hpp"
4 
5 #include <phosphor-logging/log.hpp>
6 #include <phosphor-logging/elog-errors.hpp>
7 #include <sdbusplus/bus.hpp>
8 #include <xyz/openbmc_project/Common/error.hpp>
9 #include <sdbusplus/bus.hpp>
10 #include <gmock/gmock.h>
11 #include <gtest/gtest.h>
12 
13 #include <filesystem>
14 #include <fstream>
15 #include <string>
16 #include <sys/types.h>
17 
18 namespace phosphor
19 {
20 namespace ldap
21 {
22 namespace fs = std::filesystem;
23 namespace ldap_base = sdbusplus::xyz::openbmc_project::User::Ldap::server;
24 using Config = phosphor::ldap::Config;
25 static constexpr const char* dbusPersistFile = "Config";
26 
27 class TestLDAPConfig : public testing::Test
28 {
29   public:
30     TestLDAPConfig() : bus(sdbusplus::bus::new_default())
31     {
32     }
33     void SetUp() override
34     {
35         using namespace phosphor::ldap;
36         char tmpldap[] = "/tmp/ldap_test.XXXXXX";
37         dir = fs::path(mkdtemp(tmpldap));
38         fs::path tslCacertFilePath{TLS_CACERT_FILE};
39         tslCacertFile = tslCacertFilePath.filename().c_str();
40         fs::path confFilePath{LDAP_CONFIG_FILE};
41         ldapconfFile = confFilePath.filename().c_str();
42         std::fstream fs;
43         fs.open(dir / defaultNslcdFile, std::fstream::out);
44         fs.close();
45         fs.open(dir / nsSwitchFile, std::fstream::out);
46         fs.close();
47     }
48 
49     void TearDown() override
50     {
51         fs::remove_all(dir);
52     }
53 
54   protected:
55     fs::path dir;
56     std::string tslCacertFile;
57     std::string ldapconfFile;
58     sdbusplus::bus::bus bus;
59 };
60 
61 class MockConfigMgr : public phosphor::ldap::ConfigMgr
62 {
63   public:
64     MockConfigMgr(sdbusplus::bus::bus& bus, const char* path,
65                   const char* filePath, const char* dbusPersistentFile,
66                   const char* caCertFile) :
67         phosphor::ldap::ConfigMgr(bus, path, filePath, dbusPersistentFile,
68                                   caCertFile)
69     {
70     }
71     MOCK_METHOD1(restartService, void(const std::string& service));
72     MOCK_METHOD1(stopService, void(const std::string& service));
73     std::unique_ptr<Config>& getOpenLdapConfigPtr()
74     {
75         return openLDAPConfigPtr;
76     }
77 
78     std::string configBindPassword()
79     {
80         return getADConfigPtr()->lDAPBindPassword;
81     }
82 
83     std::unique_ptr<Config>& getADConfigPtr()
84     {
85         return ADConfigPtr;
86     }
87     void restore()
88     {
89         phosphor::ldap::ConfigMgr::restore();
90         return;
91     }
92 
93     void createDefaultObjects()
94     {
95         phosphor::ldap::ConfigMgr::createDefaultObjects();
96     }
97 
98     friend class TestLDAPConfig;
99 };
100 
101 TEST_F(TestLDAPConfig, testCreate)
102 {
103     auto configFilePath = std::string(dir.c_str()) + "/" + ldapconfFile;
104     auto tlsCacertfile = std::string(dir.c_str()) + "/" + tslCacertFile;
105     auto dbusPersistentFilePath = std::string(dir.c_str());
106 
107     if (fs::exists(configFilePath))
108     {
109         fs::remove(configFilePath);
110     }
111     EXPECT_FALSE(fs::exists(configFilePath));
112     MockConfigMgr manager(bus, LDAP_CONFIG_ROOT, configFilePath.c_str(),
113                           dbusPersistentFilePath.c_str(),
114                           tlsCacertfile.c_str());
115 
116     EXPECT_CALL(manager, stopService("nslcd.service")).Times(2);
117     EXPECT_CALL(manager, restartService("nslcd.service")).Times(2);
118     EXPECT_CALL(manager, restartService("nscd.service")).Times(2);
119 
120     manager.createConfig(
121         "ldap://9.194.251.136/", "cn=Users,dc=com", "cn=Users,dc=corp",
122         "MyLdap12", ldap_base::Create::SearchScope::sub,
123         ldap_base::Create::Type::ActiveDirectory, "uid", "gid");
124     manager.getADConfigPtr()->enabled(true);
125 
126     manager.createConfig("ldap://9.194.251.137/", "cn=Users",
127                          "cn=Users,dc=test", "MyLdap123",
128                          ldap_base::Create::SearchScope::sub,
129                          ldap_base::Create::Type::OpenLdap, "uid", "gid");
130     manager.getOpenLdapConfigPtr()->enabled(false);
131 
132     // Below setting of username/groupname attr is to make sure
133     // that in-active config should not call the start/stop service.
134     manager.getOpenLdapConfigPtr()->userNameAttribute("abc");
135     EXPECT_EQ(manager.getOpenLdapConfigPtr()->userNameAttribute(), "abc");
136 
137     manager.getOpenLdapConfigPtr()->groupNameAttribute("def");
138     EXPECT_EQ(manager.getOpenLdapConfigPtr()->groupNameAttribute(), "def");
139 
140     EXPECT_TRUE(fs::exists(configFilePath));
141     EXPECT_EQ(manager.getADConfigPtr()->lDAPServerURI(),
142               "ldap://9.194.251.136/");
143     EXPECT_EQ(manager.getADConfigPtr()->lDAPBindDN(), "cn=Users,dc=com");
144     EXPECT_EQ(manager.getADConfigPtr()->lDAPBaseDN(), "cn=Users,dc=corp");
145     EXPECT_EQ(manager.getADConfigPtr()->lDAPSearchScope(),
146               ldap_base::Config::SearchScope::sub);
147     EXPECT_EQ(manager.getADConfigPtr()->lDAPType(),
148               ldap_base::Config::Type::ActiveDirectory);
149 
150     EXPECT_EQ(manager.getADConfigPtr()->userNameAttribute(), "uid");
151     EXPECT_EQ(manager.getADConfigPtr()->groupNameAttribute(), "gid");
152     EXPECT_EQ(manager.getADConfigPtr()->lDAPBindDNPassword(), "");
153     EXPECT_EQ(manager.configBindPassword(), "MyLdap12");
154     // change the password
155     manager.getADConfigPtr()->lDAPBindDNPassword("MyLdap14");
156     EXPECT_EQ(manager.getADConfigPtr()->lDAPBindDNPassword(), "");
157     EXPECT_EQ(manager.configBindPassword(), "MyLdap14");
158 }
159 
160 TEST_F(TestLDAPConfig, testDefaultObject)
161 {
162     auto configFilePath = std::string(dir.c_str()) + "/" + ldapconfFile;
163     auto tlsCacertfile = std::string(dir.c_str()) + "/" + tslCacertFile;
164     auto dbusPersistentFilePath = std::string(dir.c_str());
165 
166     if (fs::exists(configFilePath))
167     {
168         fs::remove(configFilePath);
169     }
170     EXPECT_FALSE(fs::exists(configFilePath));
171 
172     MockConfigMgr manager(bus, LDAP_CONFIG_ROOT, configFilePath.c_str(),
173                           dbusPersistentFilePath.c_str(),
174                           tlsCacertfile.c_str());
175 
176     manager.createDefaultObjects();
177 
178     EXPECT_NE(nullptr, manager.getADConfigPtr());
179     EXPECT_NE(nullptr, manager.getOpenLdapConfigPtr());
180     EXPECT_EQ(manager.getADConfigPtr()->lDAPType(),
181               ldap_base::Config::Type::ActiveDirectory);
182     EXPECT_EQ(manager.getOpenLdapConfigPtr()->lDAPType(),
183               ldap_base::Config::Type::OpenLdap);
184 }
185 
186 TEST_F(TestLDAPConfig, testRestores)
187 {
188     auto configFilePath = std::string(dir.c_str()) + "/" + ldapconfFile;
189     auto tlsCacertfile = std::string(dir.c_str()) + "/" + tslCacertFile;
190     auto dbusPersistentFilePath = std::string(dir.c_str());
191 
192     if (fs::exists(configFilePath))
193     {
194         fs::remove(configFilePath);
195     }
196     EXPECT_FALSE(fs::exists(configFilePath));
197     MockConfigMgr* managerPtr = new MockConfigMgr(
198         bus, LDAP_CONFIG_ROOT, configFilePath.c_str(),
199         dbusPersistentFilePath.c_str(), tlsCacertfile.c_str());
200     EXPECT_CALL(*managerPtr, stopService("nslcd.service")).Times(1);
201     EXPECT_CALL(*managerPtr, restartService("nslcd.service")).Times(1);
202     EXPECT_CALL(*managerPtr, restartService("nscd.service")).Times(1);
203     managerPtr->createConfig(
204         "ldap://9.194.251.138/", "cn=Users,dc=com", "cn=Users,dc=corp",
205         "MyLdap12", ldap_base::Create::SearchScope::sub,
206         ldap_base::Create::Type::ActiveDirectory, "uid", "gid");
207     managerPtr->getADConfigPtr()->enabled(false);
208     EXPECT_FALSE(fs::exists(configFilePath));
209     EXPECT_FALSE(managerPtr->getADConfigPtr()->enabled());
210     managerPtr->getADConfigPtr()->enabled(true);
211 
212     EXPECT_TRUE(fs::exists(configFilePath));
213     // Restore from configFilePath
214     managerPtr->restore();
215     // validate restored properties
216     EXPECT_TRUE(managerPtr->getADConfigPtr()->enabled());
217     EXPECT_EQ(managerPtr->getADConfigPtr()->lDAPServerURI(),
218               "ldap://9.194.251.138/");
219     EXPECT_EQ(managerPtr->getADConfigPtr()->lDAPBindDN(), "cn=Users,dc=com");
220     EXPECT_EQ(managerPtr->getADConfigPtr()->lDAPBaseDN(), "cn=Users,dc=corp");
221     EXPECT_EQ(managerPtr->getADConfigPtr()->lDAPSearchScope(),
222               ldap_base::Config::SearchScope::sub);
223     EXPECT_EQ(managerPtr->getADConfigPtr()->lDAPType(),
224               ldap_base::Config::Type::ActiveDirectory);
225     EXPECT_EQ(managerPtr->getADConfigPtr()->userNameAttribute(), "uid");
226     EXPECT_EQ(managerPtr->getADConfigPtr()->groupNameAttribute(), "gid");
227     EXPECT_EQ(managerPtr->getADConfigPtr()->lDAPBindDNPassword(), "");
228     EXPECT_EQ(managerPtr->configBindPassword(), "MyLdap12");
229     delete managerPtr;
230 }
231 
232 TEST_F(TestLDAPConfig, testLDAPServerURI)
233 {
234     auto configFilePath = std::string(dir.c_str()) + "/" + ldapconfFile;
235     auto tlsCacertfile = std::string(dir.c_str()) + "/" + tslCacertFile;
236     auto dbusPersistentFilePath = std::string(dir.c_str());
237 
238     if (fs::exists(configFilePath))
239     {
240         fs::remove(configFilePath);
241     }
242     EXPECT_FALSE(fs::exists(configFilePath));
243     MockConfigMgr* managerPtr = new MockConfigMgr(
244         bus, LDAP_CONFIG_ROOT, configFilePath.c_str(),
245         dbusPersistentFilePath.c_str(), tlsCacertfile.c_str());
246 
247     EXPECT_CALL(*managerPtr, stopService("nslcd.service")).Times(1);
248     EXPECT_CALL(*managerPtr, restartService("nslcd.service")).Times(2);
249     EXPECT_CALL(*managerPtr, restartService("nscd.service")).Times(1);
250 
251     managerPtr->createConfig(
252         "ldap://9.194.251.138/", "cn=Users,dc=com", "cn=Users,dc=corp",
253         "MyLdap12", ldap_base::Create::SearchScope::sub,
254         ldap_base::Create::Type::ActiveDirectory, "attr1", "attr2");
255     managerPtr->getADConfigPtr()->enabled(true);
256 
257     // Change LDAP Server URI
258     managerPtr->getADConfigPtr()->lDAPServerURI("ldap://9.194.251.139/");
259     EXPECT_EQ(managerPtr->getADConfigPtr()->lDAPServerURI(),
260               "ldap://9.194.251.139/");
261 
262     // Change LDAP Server URI to make it secure
263     EXPECT_THROW(
264         managerPtr->getADConfigPtr()->lDAPServerURI("ldaps://9.194.251.139/"),
265         NoCACertificate);
266 
267     // check once again
268     EXPECT_EQ(managerPtr->getADConfigPtr()->lDAPServerURI(),
269               "ldap://9.194.251.139/");
270 
271     managerPtr->restore();
272     // Check LDAP Server URI
273     EXPECT_EQ(managerPtr->getADConfigPtr()->lDAPServerURI(),
274               "ldap://9.194.251.139/");
275     delete managerPtr;
276 }
277 
278 TEST_F(TestLDAPConfig, testLDAPBindDN)
279 {
280     auto configFilePath = std::string(dir.c_str()) + "/" + ldapconfFile;
281     auto tlsCacertfile = std::string(dir.c_str()) + "/" + tslCacertFile;
282     auto dbusPersistentFilePath = std::string(dir.c_str());
283 
284     if (fs::exists(configFilePath))
285     {
286         fs::remove(configFilePath);
287     }
288     EXPECT_FALSE(fs::exists(configFilePath));
289     MockConfigMgr* managerPtr = new MockConfigMgr(
290         bus, LDAP_CONFIG_ROOT, configFilePath.c_str(),
291         dbusPersistentFilePath.c_str(), tlsCacertfile.c_str());
292 
293     EXPECT_CALL(*managerPtr, stopService("nslcd.service")).Times(1);
294     EXPECT_CALL(*managerPtr, restartService("nslcd.service")).Times(2);
295     EXPECT_CALL(*managerPtr, restartService("nscd.service")).Times(1);
296 
297     managerPtr->createConfig(
298         "ldap://9.194.251.138/", "cn=Users,dc=com", "cn=Users,dc=corp",
299         "MyLdap12", ldap_base::Create::SearchScope::sub,
300         ldap_base::Create::Type::ActiveDirectory, "attr1", "attr2");
301     managerPtr->getADConfigPtr()->enabled(true);
302 
303     // Change LDAP BindDN
304     managerPtr->getADConfigPtr()->lDAPBindDN(
305         "cn=Administrator,cn=Users,dc=corp,dc=ibm,dc=com");
306     EXPECT_EQ(managerPtr->getADConfigPtr()->lDAPBindDN(),
307               "cn=Administrator,cn=Users,dc=corp,dc=ibm,dc=com");
308     // Change LDAP BindDN
309     EXPECT_THROW(
310         {
311             try
312             {
313                 managerPtr->getADConfigPtr()->lDAPBindDN("");
314             }
315             catch (const InvalidArgument& e)
316             {
317                 throw;
318             }
319         },
320         InvalidArgument);
321 
322     managerPtr->restore();
323     // Check LDAP BindDN after restoring
324     EXPECT_EQ(managerPtr->getADConfigPtr()->lDAPBindDN(),
325               "cn=Administrator,cn=Users,dc=corp,dc=ibm,dc=com");
326     delete managerPtr;
327 }
328 
329 TEST_F(TestLDAPConfig, testLDAPBaseDN)
330 {
331     auto configFilePath = std::string(dir.c_str()) + "/" + ldapconfFile;
332     auto tlsCacertfile = std::string(dir.c_str()) + "/" + tslCacertFile;
333     auto dbusPersistentFilePath = std::string(dir.c_str());
334 
335     if (fs::exists(configFilePath))
336     {
337         fs::remove(configFilePath);
338     }
339     EXPECT_FALSE(fs::exists(configFilePath));
340     MockConfigMgr* managerPtr = new MockConfigMgr(
341         bus, LDAP_CONFIG_ROOT, configFilePath.c_str(),
342         dbusPersistentFilePath.c_str(), tlsCacertfile.c_str());
343     EXPECT_CALL(*managerPtr, stopService("nslcd.service")).Times(1);
344     EXPECT_CALL(*managerPtr, restartService("nslcd.service")).Times(2);
345     EXPECT_CALL(*managerPtr, restartService("nscd.service")).Times(1);
346     managerPtr->createConfig(
347         "ldap://9.194.251.138/", "cn=Users,dc=com", "cn=Users,dc=corp",
348         "MyLdap12", ldap_base::Create::SearchScope::sub,
349         ldap_base::Create::Type::ActiveDirectory, "attr1", "attr2");
350     managerPtr->getADConfigPtr()->enabled(true);
351     // Change LDAP BaseDN
352     managerPtr->getADConfigPtr()->lDAPBaseDN(
353         "cn=Administrator,cn=Users,dc=corp,dc=ibm,dc=com");
354     EXPECT_EQ(managerPtr->getADConfigPtr()->lDAPBaseDN(),
355               "cn=Administrator,cn=Users,dc=corp,dc=ibm,dc=com");
356     // Change LDAP BaseDN
357     EXPECT_THROW(
358         {
359             try
360             {
361                 managerPtr->getADConfigPtr()->lDAPBaseDN("");
362             }
363             catch (const InvalidArgument& e)
364             {
365                 throw;
366             }
367         },
368         InvalidArgument);
369 
370     managerPtr->restore();
371     // Check LDAP BaseDN after restoring
372     EXPECT_EQ(managerPtr->getADConfigPtr()->lDAPBaseDN(),
373               "cn=Administrator,cn=Users,dc=corp,dc=ibm,dc=com");
374     delete managerPtr;
375 }
376 
377 TEST_F(TestLDAPConfig, testSearchScope)
378 {
379     auto configFilePath = std::string(dir.c_str()) + "/" + ldapconfFile;
380     auto tlsCacertfile = std::string(dir.c_str()) + "/" + tslCacertFile;
381     auto dbusPersistentFilePath = std::string(dir.c_str());
382 
383     if (fs::exists(configFilePath))
384     {
385         fs::remove(configFilePath);
386     }
387     EXPECT_FALSE(fs::exists(configFilePath));
388     MockConfigMgr* managerPtr = new MockConfigMgr(
389         bus, LDAP_CONFIG_ROOT, configFilePath.c_str(),
390         dbusPersistentFilePath.c_str(), tlsCacertfile.c_str());
391     EXPECT_CALL(*managerPtr, stopService("nslcd.service")).Times(1);
392     EXPECT_CALL(*managerPtr, restartService("nslcd.service")).Times(2);
393     EXPECT_CALL(*managerPtr, restartService("nscd.service")).Times(1);
394     managerPtr->createConfig(
395         "ldap://9.194.251.138/", "cn=Users,dc=com", "cn=Users,dc=corp",
396         "MyLdap12", ldap_base::Create::SearchScope::sub,
397         ldap_base::Create::Type::ActiveDirectory, "attr1", "attr2");
398     managerPtr->getADConfigPtr()->enabled(true);
399 
400     // Change LDAP SearchScope
401     managerPtr->getADConfigPtr()->lDAPSearchScope(
402         ldap_base::Config::SearchScope::one);
403     EXPECT_EQ(managerPtr->getADConfigPtr()->lDAPSearchScope(),
404               ldap_base::Config::SearchScope::one);
405 
406     managerPtr->restore();
407     // Check LDAP SearchScope after restoring
408     EXPECT_EQ(managerPtr->getADConfigPtr()->lDAPSearchScope(),
409               ldap_base::Config::SearchScope::one);
410     delete managerPtr;
411 }
412 
413 TEST_F(TestLDAPConfig, testLDAPType)
414 {
415     auto configFilePath = std::string(dir.c_str()) + "/" + ldapconfFile;
416     auto tlsCacertfile = std::string(dir.c_str()) + "/" + tslCacertFile;
417     auto dbusPersistentFilePath = std::string(dir.c_str());
418 
419     if (fs::exists(configFilePath))
420     {
421         fs::remove(configFilePath);
422     }
423     EXPECT_FALSE(fs::exists(configFilePath));
424     MockConfigMgr* managerPtr = new MockConfigMgr(
425         bus, LDAP_CONFIG_ROOT, configFilePath.c_str(),
426         dbusPersistentFilePath.c_str(), tlsCacertfile.c_str());
427     EXPECT_CALL(*managerPtr, stopService("nslcd.service")).Times(1);
428     EXPECT_CALL(*managerPtr, restartService("nslcd.service")).Times(1);
429     EXPECT_CALL(*managerPtr, restartService("nscd.service")).Times(1);
430     managerPtr->createConfig(
431         "ldap://9.194.251.138/", "cn=Users,dc=com", "cn=Users,dc=corp",
432         "MyLdap12", ldap_base::Create::SearchScope::sub,
433         ldap_base::Create::Type::ActiveDirectory, "attr1", "attr2");
434     managerPtr->getADConfigPtr()->enabled(true);
435 
436     // Change LDAP type
437     // will not be changed
438     EXPECT_THROW(managerPtr->getADConfigPtr()->lDAPType(
439                      ldap_base::Config::Type::OpenLdap),
440                  NotAllowed);
441     EXPECT_EQ(managerPtr->getADConfigPtr()->lDAPType(),
442               ldap_base::Config::Type::ActiveDirectory);
443 
444     managerPtr->restore();
445     // Check LDAP type after restoring
446     EXPECT_EQ(managerPtr->getADConfigPtr()->lDAPType(),
447               ldap_base::Config::Type::ActiveDirectory);
448     delete managerPtr;
449 }
450 
451 TEST_F(TestLDAPConfig, filePermission)
452 {
453     auto configFilePath = std::string(dir.c_str()) + "/" + ldapconfFile;
454     auto tlsCacertfile = std::string(dir.c_str()) + "/" + tslCacertFile;
455     auto dbusPersistentFilePath = std::string(dir.c_str());
456 
457     if (fs::exists(configFilePath))
458     {
459         fs::remove(configFilePath);
460     }
461     EXPECT_FALSE(fs::exists(configFilePath));
462     MockConfigMgr* managerPtr = new MockConfigMgr(
463         bus, LDAP_CONFIG_ROOT, configFilePath.c_str(),
464         dbusPersistentFilePath.c_str(), tlsCacertfile.c_str());
465     EXPECT_CALL(*managerPtr, stopService("nslcd.service")).Times(1);
466     EXPECT_CALL(*managerPtr, restartService("nslcd.service")).Times(1);
467     EXPECT_CALL(*managerPtr, restartService("nscd.service")).Times(1);
468     managerPtr->createConfig(
469         "ldap://9.194.251.138/", "cn=Users,dc=com", "cn=Users,dc=corp",
470         "MyLdap12", ldap_base::Create::SearchScope::sub,
471         ldap_base::Create::Type::ActiveDirectory, "attr1", "attr2");
472     managerPtr->getADConfigPtr()->enabled(true);
473 
474     // Permission of the persistent file should be 640
475     // Others should not be allowed to read.
476     auto permission =
477         fs::perms::owner_read | fs::perms::owner_write | fs::perms::group_read;
478     auto persistFilepath = std::string(dir.c_str());
479     persistFilepath += ADDbusObjectPath;
480     persistFilepath += "/config";
481 
482     EXPECT_EQ(fs::status(persistFilepath).permissions(), permission);
483     delete managerPtr;
484 }
485 
486 } // namespace ldap
487 } // namespace phosphor
488