application_config.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  1. /*##############################################################################
  2. HPCC SYSTEMS software Copyright (C) 2020 HPCC Systems®.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. ############################################################################## */
  13. #pragma warning (disable : 4786)
  14. #include "jliball.hpp"
  15. // We can use "work" as the first parameter when debugging.
  16. #define ESP_SINGLE_PROCESS
  17. //ESP Core
  18. #include "espp.hpp"
  19. #include "espcfg.ipp"
  20. #include "esplog.hpp"
  21. #include "espcontext.hpp"
  22. #include "build-config.h"
  23. enum class LdapType { LegacyAD, AzureAD };
  24. static void appendPTreeFromYamlFile(IPropertyTree *tree, const char *file)
  25. {
  26. Owned<IPropertyTree> appendTree = createPTreeFromYAMLFile(file);
  27. if (appendTree->hasProp("esp"))
  28. mergeConfiguration(*tree, *appendTree->queryPropTree("esp"), nullptr);
  29. else
  30. mergeConfiguration(*tree, *appendTree, nullptr);
  31. }
  32. IPropertyTree *loadApplicationConfig(const char *application, const char* argv[])
  33. {
  34. Owned<IPropertyTree> applicationConfig = createPTree(application);
  35. IPropertyTree *defaultConfig = applicationConfig->addPropTree("esp");
  36. char sepchar = getPathSepChar(COMPONENTFILES_DIR);
  37. StringBuffer path(COMPONENTFILES_DIR);
  38. addPathSepChar(path, sepchar).append("applications").append(sepchar).append("common").append(sepchar);
  39. if (checkDirExists(path))
  40. {
  41. Owned<IDirectoryIterator> common_dir = createDirectoryIterator(path, "*.yaml", false, false);
  42. ForEach(*common_dir)
  43. appendPTreeFromYamlFile(defaultConfig, common_dir->query().queryFilename());
  44. }
  45. path.set(COMPONENTFILES_DIR);
  46. addPathSepChar(path, sepchar).append("applications").append(sepchar).append(application).append(sepchar);
  47. if (!checkDirExists(path))
  48. throw MakeStringException(-1, "Can't find esp application %s (dir %s)", application, path.str());
  49. Owned<IDirectoryIterator> application_dir = createDirectoryIterator(path, "*.yaml", false, false);
  50. ForEach(*application_dir)
  51. appendPTreeFromYamlFile(defaultConfig, application_dir->query().queryFilename());
  52. //apply provided config to the application
  53. Owned<IPropertyTree> config = loadConfiguration(defaultConfig, argv, "esp", "ESP", nullptr, nullptr);
  54. return config.getClear();
  55. }
  56. static void copyAttributes(IPropertyTree *target, IPropertyTree *src)
  57. {
  58. Owned<IAttributeIterator> aiter = src->getAttributes();
  59. ForEach (*aiter)
  60. target->addProp(aiter->queryName(), aiter->queryValue());
  61. }
  62. static void copyDirectories(IPropertyTree *target, IPropertyTree *src)
  63. {
  64. if (!src || !target)
  65. return;
  66. Owned<IAttributeIterator> aiter = src->getAttributes();
  67. ForEach (*aiter)
  68. {
  69. IPropertyTree *entry = target->addPropTree("Category");
  70. entry->setProp("@name", aiter->queryName()+1);
  71. entry->setProp("@dir", aiter->queryValue());
  72. }
  73. }
  74. bool addLdapSecurity(IPropertyTree *legacyEsp, IPropertyTree *appEsp, StringBuffer &bindAuth, LdapType ldapType)
  75. {
  76. const char *ldapAddress = appEsp->queryProp("@ldapAddress");
  77. if (isEmptyString(ldapAddress))
  78. throw MakeStringException(-1, "LDAP not configured. To run without security set auth=none");
  79. StringBuffer path(COMPONENTFILES_DIR);
  80. char sepchar = getPathSepChar(COMPONENTFILES_DIR);
  81. addPathSepChar(path, sepchar).append("applications").append(sepchar).append("common").append(sepchar).append("ldap").append(sepchar);
  82. if (ldapType == LdapType::LegacyAD)
  83. path.append("ldap.yaml");
  84. else
  85. path.append("azure_ldap.yaml");
  86. if (checkFileExists(path))
  87. appendPTreeFromYamlFile(appEsp, path.str());
  88. IPropertyTree *appLdap = appEsp->queryPropTree("ldap");
  89. if (!appLdap)
  90. throw MakeStringException(-1, "Can't find application LDAP settings. To run without security set auth=none");
  91. IPropertyTree *legacyLdap = legacyEsp->addPropTree("ldapSecurity");
  92. copyAttributes(legacyLdap, appLdap);
  93. legacyLdap->setProp("@ldapAddress", ldapAddress);
  94. StringAttr configname(appLdap->queryProp("@objname"));
  95. if (!legacyLdap->hasProp("@name"))
  96. legacyLdap->setProp("@name", configname.str());
  97. StringAttr resourcesBasedn(appLdap->queryProp("@resourcesBasedn"));
  98. StringAttr workunitsBasedn(appLdap->queryProp("@workunitsBasedn"));
  99. bindAuth.setf("<Authenticate method='LdapSecurity' config='%s' resourcesBasedn='%s' workunitsBasedn='%s'/>", configname.str(), resourcesBasedn.str(), workunitsBasedn.str());
  100. VStringBuffer authenticationXml("<Authentication htpasswdFile='/etc/HPCCSystems/.htpasswd' ldapAuthMethod='kerberos' ldapConnections='10' ldapServer='%s' method='ldaps' passwordExpirationWarningDays='10'/>", configname.str());
  101. legacyEsp->addPropTree("Authentication", createPTreeFromXMLString(authenticationXml));
  102. return true;
  103. }
  104. bool addAuthNZSecurity(const char *name, IPropertyTree *legacyEsp, IPropertyTree *appEsp, StringBuffer &bindAuth)
  105. {
  106. IPropertyTree *authNZ = appEsp->queryPropTree("authNZ");
  107. if (!authNZ)
  108. throw MakeStringException(-1, "Can't find application AuthNZ section. To run without security set auth=none");
  109. authNZ = authNZ->queryPropTree(name);
  110. if (!authNZ)
  111. throw MakeStringException(-1, "Can't find application %s AuthNZ settings. To run without security set auth=none", name);
  112. IPropertyTree *appSecMgr = authNZ->queryPropTree("SecurityManager");
  113. if (!appSecMgr)
  114. {
  115. const char *application = appEsp->queryProp("@application");
  116. throw MakeStringException(-1, "Can't find SecurityManager settings configuring application '%s'. To run without security set auth=none", application ? application : "");
  117. }
  118. const char *method = appSecMgr->queryProp("@name");
  119. const char *tag = appSecMgr->queryProp("@type");
  120. if (isEmptyString(tag))
  121. throw MakeStringException(-1, "SecurityManager type attribute required. To run without security set auth=none");
  122. legacyEsp->addPropTree("AuthDomains", createPTreeFromXMLString("<AuthDomains><AuthDomain authType='AuthPerRequestOnly' clientSessionTimeoutMinutes='120' domainName='default' invalidURLsAfterAuth='/esp/login' loginLogoURL='/esp/files/eclwatch/img/Loginlogo.png' logonURL='/esp/files/Login.html' logoutURL='' serverSessionTimeoutMinutes='240' unrestrictedResources='/favicon.ico,/esp/files/*,/esp/xslt/*'/></AuthDomains>"));
  123. IPropertyTree *legacy = legacyEsp->addPropTree("SecurityManagers");
  124. legacy = legacy->addPropTree("SecurityManager");
  125. copyAttributes(legacy, appSecMgr);
  126. legacy = legacy->addPropTree(tag);
  127. mergePTree(legacy, authNZ); //extra info clean up later
  128. legacy->removeProp("SecurityManager"); //already copied these attributes above, don't need this as a child
  129. bindAuth.setf("<Authenticate method='%s'/>", method ? method : "unknown");
  130. return true;
  131. }
  132. // auth "none" must be explicit, default to secure mode, don't want to accidentally turn off security
  133. bool addSecurity(IPropertyTree *legacyEsp, IPropertyTree *appEsp, StringBuffer &bindAuth)
  134. {
  135. const char *auth = appEsp->queryProp("@auth");
  136. if (isEmptyString(auth))
  137. throw MakeStringException(-1, "'auth' attribute required. To run without security set 'auth=none'");
  138. if (streq(auth, "none"))
  139. return false;
  140. if (streq(auth, "ldap"))
  141. return addLdapSecurity(legacyEsp, appEsp, bindAuth, LdapType::LegacyAD);
  142. if (streq(auth, "azure_ldap"))
  143. return addLdapSecurity(legacyEsp, appEsp, bindAuth, LdapType::AzureAD);
  144. return addAuthNZSecurity(auth, legacyEsp, appEsp, bindAuth);
  145. }
  146. void bindAuthResources(IPropertyTree *legacyAuthenticate, IPropertyTree *app, const char *service, const char *auth)
  147. {
  148. IPropertyTree *appAuth = nullptr;
  149. if (isEmptyString(auth) || streq(auth, "ldap") || streq(auth, "azure_ldap"))
  150. appAuth = app->queryPropTree("ldap");
  151. else if (streq(auth, "none"))
  152. return;
  153. else
  154. {
  155. appAuth = app->queryPropTree("authNZ");
  156. if (!appAuth)
  157. return;
  158. appAuth = appAuth->queryPropTree(auth);
  159. if (!appAuth)
  160. return;
  161. }
  162. if (!appAuth)
  163. throw MakeStringException(-1, "Can't find application Auth settings. To run without security set auth=none");
  164. IPropertyTree *root_access = appAuth->queryPropTree("root_access");
  165. StringAttr required(root_access->queryProp("@required"));
  166. StringAttr description(root_access->queryProp("@description"));
  167. StringAttr resource(root_access->queryProp("@resource"));
  168. VStringBuffer locationXml("<Location path='/' resource='%s' required='%s' description='%s'/>", resource.str(), required.str(), description.str());
  169. legacyAuthenticate->addPropTree("Location", createPTreeFromXMLString(locationXml));
  170. VStringBuffer featuresPath("resource_map/%s/Feature", service);
  171. Owned<IPropertyTreeIterator> features = appAuth->getElements(featuresPath);
  172. ForEach(*features)
  173. legacyAuthenticate->addPropTree("Feature", LINK(&features->query()));
  174. }
  175. void bindService(IPropertyTree *legacyEsp, IPropertyTree *app, const char *service, const char *protocol, const char *netAddress, unsigned port, const char *bindAuth, int seq)
  176. {
  177. VStringBuffer xpath("binding_plugins/@%s", service);
  178. const char *binding_plugin = app->queryProp(xpath);
  179. VStringBuffer bindingXml("<EspBinding name='%s_binding' service='%s_service' protocol='%s' type='%s_http' plugin='%s' netAddress='%s' port='%d'/>", service, service, protocol, service, binding_plugin, netAddress, port);
  180. IPropertyTree *bindingEntry = legacyEsp->addPropTree("EspBinding", createPTreeFromXMLString(bindingXml));
  181. if (seq==0)
  182. bindingEntry->setProp("@defaultBinding", "true");
  183. if (!isEmptyString(bindAuth))
  184. {
  185. const char *auth = app->queryProp("@auth");
  186. IPropertyTree *authenticate = bindingEntry->addPropTree("Authenticate", createPTreeFromXMLString(bindAuth));
  187. if (authenticate)
  188. bindAuthResources(authenticate, app, service, auth);
  189. }
  190. }
  191. static void mergeServicePTree(IPropertyTree *target, IPropertyTree *toMerge)
  192. {
  193. Owned<IAttributeIterator> aiter = toMerge->getAttributes();
  194. ForEach (*aiter)
  195. {
  196. //make uppercase attributes into elements to match legacy ESP config
  197. const char *xpath = aiter->queryName();
  198. if (xpath && xpath[0]=='@' && isupper(xpath[1]))
  199. xpath++;
  200. target->addProp(xpath, aiter->queryValue());
  201. }
  202. Owned<IPropertyTreeIterator> iter = toMerge->getElements("*");
  203. ForEach (*iter)
  204. {
  205. IPropertyTree &e = iter->query();
  206. target->addPropTree(e.queryName(), LINK(&e));
  207. }
  208. }
  209. void addService(IPropertyTree *legacyEsp, IPropertyTree *app, const char *application, const char *service, const char *protocol, const char *netAddress, unsigned port, const char *bindAuth, int seq)
  210. {
  211. VStringBuffer plugin_xpath("service_plugins/@%s", service);
  212. const char *service_plugin = app->queryProp(plugin_xpath);
  213. VStringBuffer serviceXml("<EspService name='%s_service' type='%s' plugin='%s'/>", service, service, service_plugin);
  214. VStringBuffer config_xpath("%s/%s", application, service);
  215. IPropertyTree *serviceConfig = app->queryPropTree(config_xpath);
  216. IPropertyTree *serviceEntry = legacyEsp->addPropTree("EspService", createPTreeFromXMLString(serviceXml));
  217. if (serviceConfig && serviceEntry)
  218. mergeServicePTree(serviceEntry, serviceConfig);
  219. bindService(legacyEsp, app, service, protocol, netAddress, port, bindAuth, seq);
  220. }
  221. bool addProtocol(IPropertyTree *legacyEsp, IPropertyTree *app)
  222. {
  223. bool useTls = app->getPropBool("@tls", true);
  224. if (useTls)
  225. {
  226. StringBuffer protocolXml("<EspProtocol name='https' type='secure_http_protocol' plugin='esphttp' maxRequestEntityLength='60000000'/>");
  227. IPropertyTree *protocol = legacyEsp->addPropTree("EspProtocol", createPTreeFromXMLString(protocolXml));
  228. IPropertyTree *tls = app->queryPropTree("tls_config");
  229. if (protocol && tls)
  230. {
  231. Owned<IAttributeIterator> aiter = tls->getAttributes();
  232. ForEach (*aiter)
  233. tls->addProp(aiter->queryName()+1, aiter->queryValue()); //attributes to elements to match legacy config
  234. Owned<IPropertyTree> temp;
  235. const char *instance = app->queryProp("@instance");
  236. if (instance)
  237. {
  238. StringBuffer xml;
  239. toXML(tls, xml);
  240. xml.replaceString("{$instance}", instance);
  241. temp.setown(createPTreeFromXMLString(xml));
  242. tls = temp.get();
  243. }
  244. mergePTree(protocol, tls);
  245. }
  246. }
  247. else
  248. {
  249. StringBuffer protocolXml("<EspProtocol name='http' type='http_protocol' plugin='esphttp' maxRequestEntityLength='60000000'/>");
  250. legacyEsp->addPropTree("EspProtocol", createPTreeFromXMLString(protocolXml));
  251. }
  252. return useTls;
  253. }
  254. void addServices(IPropertyTree *legacyEsp, IPropertyTree *appEsp, const char *application, const char *auth, bool tls)
  255. {
  256. Owned<IPropertyTreeIterator> services = appEsp->getElements("application/services");
  257. int port = appEsp->getPropInt("@port", 8880);
  258. const char *netAddress = appEsp->queryProp("@netAddress");
  259. if (!netAddress)
  260. netAddress = ".";
  261. int seq=0;
  262. ForEach(*services)
  263. addService(legacyEsp, appEsp, application, services->query().queryProp("."), tls ? "https" : "http", netAddress, port, auth, seq++);
  264. }
  265. IPropertyTree *buildApplicationLegacyConfig(const char *application, const char* argv[])
  266. {
  267. Owned<IPropertyTree> appEspConfig = loadApplicationConfig(application, argv);
  268. Owned<IPropertyTree> legacy = createPTreeFromXMLString("<Environment><Software><EspProcess/><Directories name='HPCCSystems'/></Software></Environment>");
  269. IPropertyTree *legacyEsp = legacy->queryPropTree("Software/EspProcess");
  270. copyAttributes(legacyEsp, appEspConfig);
  271. if (!legacyEsp->hasProp("@name") && legacyEsp->hasProp("@instance"))
  272. legacyEsp->setProp("@name", legacyEsp->queryProp("@instance"));
  273. if (!legacyEsp->hasProp("@directory"))
  274. {
  275. const char *componentName = legacyEsp->queryProp("@name");
  276. VStringBuffer s("%s/%s", RUNTIME_DIR, isEmptyString(componentName) ? application : componentName);
  277. legacyEsp->setProp("@directory", s.str());
  278. }
  279. if (!legacyEsp->hasProp("@componentfilesDir"))
  280. legacyEsp->setProp("@componentfilesDir", COMPONENTFILES_DIR);
  281. bool tls = addProtocol(legacyEsp, appEspConfig);
  282. StringBuffer bindAuth;
  283. addSecurity(legacyEsp, appEspConfig, bindAuth);
  284. addServices(legacyEsp, appEspConfig, application, bindAuth, tls);
  285. IPropertyTree *legacyDirectories = legacy->queryPropTree("Software/Directories");
  286. IPropertyTree *appDirectories = appEspConfig->queryPropTree("directories");
  287. copyDirectories(legacyDirectories, appDirectories);
  288. return legacy.getClear();
  289. }