conf.py 43 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252
  1. #!/usr/bin/env python
  2. # Licensed to Cloudera, Inc. under one
  3. # or more contributor license agreements. See the NOTICE file
  4. # distributed with this work for additional information
  5. # regarding copyright ownership. Cloudera, Inc. licenses this file
  6. # to you under the Apache License, Version 2.0 (the
  7. # "License"); you may not use this file except in compliance
  8. # with the License. You may obtain a copy of the License at
  9. #
  10. # http://www.apache.org/licenses/LICENSE-2.0
  11. #
  12. # Unless required by applicable law or agreed to in writing, software
  13. # distributed under the License is distributed on an "AS IS" BASIS,
  14. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. # See the License for the specific language governing permissions and
  16. # limitations under the License.
  17. import datetime
  18. import logging
  19. import os
  20. import socket
  21. import stat
  22. import subprocess
  23. from django.utils.translation import ugettext_lazy as _
  24. from desktop.redaction.engine import parse_redaction_policy_from_file
  25. from desktop.lib.conf import Config, ConfigSection, UnspecifiedConfigSection,\
  26. coerce_bool, coerce_csv, coerce_json_dict,\
  27. validate_path, list_of_compiled_res, coerce_str_lowercase
  28. from desktop.lib.i18n import force_unicode
  29. from desktop.lib.paths import get_desktop_root
  30. def coerce_database(database):
  31. if database == 'mysql':
  32. return 'django.db.backends.mysql'
  33. elif database == 'postgres' or database == 'postgresql_psycopg2':
  34. return 'django.db.backends.postgresql_psycopg2'
  35. elif database == 'oracle':
  36. return 'django.db.backends.oracle'
  37. elif database in ('sqlite', 'sqlite3'):
  38. return 'django.db.backends.sqlite3'
  39. else:
  40. return str(database)
  41. def coerce_port(port):
  42. port = int(port)
  43. if port == 0:
  44. return ''
  45. else:
  46. return port
  47. def coerce_file(path):
  48. if path and not os.path.isfile(path):
  49. raise Exception('File %s does not exist.' % path)
  50. return path
  51. def coerce_password_from_script(script):
  52. p = subprocess.Popen(script, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
  53. stdout, stderr = p.communicate()
  54. if p.returncode != 0:
  55. if os.environ.get('HUE_IGNORE_PASSWORD_SCRIPT_ERRORS') is None:
  56. raise subprocess.CalledProcessError(p.returncode, script)
  57. else:
  58. return None
  59. # whitespace may be significant in the password, but most files have a
  60. # trailing newline.
  61. return stdout.strip('\n')
  62. def coerce_timedelta(value):
  63. return datetime.timedelta(seconds=int(value))
  64. def coerce_positive_integer(integer):
  65. integer = int(integer)
  66. if integer <= 0:
  67. raise Exception('integer is not positive')
  68. return integer
  69. HTTP_HOST = Config(
  70. key="http_host",
  71. help=_("HTTP host to bind to."),
  72. type=str,
  73. default="0.0.0.0")
  74. HTTP_PORT = Config(
  75. key="http_port",
  76. help=_("HTTP port to bind to."),
  77. type=int,
  78. default=8888)
  79. HTTP_ALLOWED_METHODS = Config(
  80. key="http_allowed_methods",
  81. help=_("HTTP methods the server will be allowed to service."),
  82. type=coerce_csv,
  83. private=True,
  84. default=['OPTIONS', 'GET', 'HEAD', 'POST', 'PUT', 'DELETE', 'CONNECT'])
  85. SSL_CERTIFICATE = Config(
  86. key="ssl_certificate",
  87. help=_("Filename of SSL Certificate"),
  88. type=coerce_file,
  89. default=None)
  90. SSL_PRIVATE_KEY = Config(
  91. key="ssl_private_key",
  92. help=_("Filename of SSL RSA Private Key"),
  93. type=coerce_file,
  94. default=None)
  95. SSL_CERTIFICATE_CHAIN = Config(
  96. key="ssl_certificate_chain",
  97. help=_("Filename of SSL Certificate Chain"),
  98. type=coerce_file,
  99. default=None)
  100. SSL_CIPHER_LIST = Config(
  101. key="ssl_cipher_list",
  102. help=_("List of allowed and disallowed ciphers"),
  103. # From https://wiki.mozilla.org/Security/Server_Side_TLS v3.7 default
  104. # recommendation, which should be compatible with Firefox 1, Chrome 1, IE 7,
  105. # Opera 5 and Safari 1.
  106. default=(
  107. "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:"
  108. "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:"
  109. "DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:"
  110. "kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:"
  111. "ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:"
  112. "ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:"
  113. "ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:"
  114. "DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:"
  115. "DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:"
  116. "AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:"
  117. "!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:"
  118. "!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA"
  119. ))
  120. SSL_PASSWORD = Config(
  121. key="ssl_password",
  122. help=_("SSL password of the the certificate"),
  123. default=None)
  124. SSL_PASSWORD_SCRIPT = Config(
  125. key="ssl_password_script",
  126. help=_("Execute this script to produce the SSL password. This will be used when `ssl_password` is not set."),
  127. type=coerce_password_from_script,
  128. default=None)
  129. SSL_CACERTS = Config(
  130. key="ssl_cacerts",
  131. help=_('Path to default Certificate Authority certificates.'),
  132. type=str,
  133. default='/etc/hue/cacerts.pem')
  134. SSL_VALIDATE = Config(
  135. key="ssl_validate",
  136. help=_('Choose whether Hue should validate certificates received from the server.'),
  137. type=coerce_bool,
  138. default=True)
  139. # Deprecated by AUTH_PASSWORD
  140. LDAP_PASSWORD = Config(
  141. key="ldap_password",
  142. help=_("LDAP password of the hue user used for LDAP authentications. For example for LDAP Authentication with HiveServer2/Impala."),
  143. private=True,
  144. default=None)
  145. # Deprecated by AUTH_PASSWORD_SCRIPT
  146. LDAP_PASSWORD_SCRIPT = Config(
  147. key="ldap_password_script",
  148. help=_("Execute this script to produce the LDAP password. This will be used when `ldap_password` is not set."),
  149. private=True,
  150. type=coerce_password_from_script,
  151. default=None)
  152. # Deprecated by by AUTH_USERNAME
  153. LDAP_USERNAME = Config(
  154. key="ldap_username",
  155. help=_("LDAP username of the hue user used for LDAP authentications. For example for LDAP Authentication with HiveServer2/Impala."),
  156. private=True,
  157. default="hue")
  158. def get_auth_username():
  159. """Backward compatibility"""
  160. return LDAP_USERNAME.get()
  161. AUTH_USERNAME = Config(
  162. key="auth_username",
  163. help=_("Auth username of the hue user used for authentications. For example for LDAP Authentication with HiveServer2/Impala."),
  164. dynamic_default=get_auth_username)
  165. def get_auth_password():
  166. """Get from script or backward compatibility"""
  167. password = os.environ.get('HUE_AUTH_PASSWORD')
  168. if password is not None:
  169. return password
  170. password = AUTH_PASSWORD_SCRIPT.get()
  171. if password:
  172. return password
  173. password = os.environ.get('HUE_LDAP_PASSWORD')
  174. if password is not None:
  175. return password
  176. password = LDAP_PASSWORD.get() # 2 levels for backward compatibility
  177. if password:
  178. return password
  179. return LDAP_PASSWORD_SCRIPT.get()
  180. AUTH_PASSWORD = Config(
  181. key="auth_password",
  182. help=_("LDAP/PAM/.. password of the hue user used for authentications. Inactive if empty. For example for LDAP Authentication with HiveServer2/Impala."),
  183. private=True,
  184. dynamic_default=get_auth_password)
  185. AUTH_PASSWORD_SCRIPT = Config(
  186. key="auth_password_script",
  187. help=_("Execute this script to produce the auth password. This will be used when `auth_password` is not set."),
  188. private=True,
  189. type=coerce_password_from_script,
  190. default=None)
  191. ENABLE_SERVER = Config(
  192. key="enable_server",
  193. help=_("If set to false, runcpserver will not actually start the web server. Used if Apache is being used as a WSGI container."),
  194. type=coerce_bool,
  195. default=True)
  196. CHERRYPY_SERVER_THREADS = Config(
  197. key="cherrypy_server_threads",
  198. help=_("Number of threads used by the CherryPy web server."),
  199. type=int,
  200. default=40)
  201. SECRET_KEY = Config(
  202. key="secret_key",
  203. help=_("Used in hashing algorithms for sessions."),
  204. default="")
  205. SECRET_KEY_SCRIPT = Config(
  206. key="secret_key_script",
  207. help=_("Execute this script to produce the Django secret key. This will be used when `secret_key` is not set."),
  208. type=coerce_password_from_script,
  209. private=True,
  210. default="")
  211. USER_ACCESS_HISTORY_SIZE = Config(
  212. key="user_access_history_size",
  213. help=_("Number of user access to remember per view per user."),
  214. type=int,
  215. default=10)
  216. COLLECT_USAGE = Config(
  217. key="collect_usage",
  218. help=_("Help improve Hue with anonymous usage analytics."
  219. "Use Google Analytics to see how many times an application or specific section of an application is used, nothing more."),
  220. type=coerce_bool,
  221. default=True)
  222. POLL_ENABLED = Config(
  223. key="poll_enabled",
  224. help=_("Use poll(2) in Hue thrift pool."),
  225. type=coerce_bool,
  226. private=True,
  227. default=True)
  228. MIDDLEWARE = Config(
  229. key="middleware",
  230. help=_("Comma-separated list of Django middleware classes to use. " +
  231. "See https://docs.djangoproject.com/en/1.4/ref/middleware/ for " +
  232. "more details on middlewares in Django."),
  233. type=coerce_csv,
  234. default=[])
  235. REDIRECT_WHITELIST = Config(
  236. key="redirect_whitelist",
  237. help=_("Comma-separated list of regular expressions, which match the redirect URL."
  238. "For example, to restrict to your local domain and FQDN, the following value can be used:"
  239. " ^\/.*$,^http:\/\/www.mydomain.com\/.*$"),
  240. type=list_of_compiled_res(skip_empty=True),
  241. default='^\/.*$')
  242. USE_X_FORWARDED_HOST = Config(
  243. key="use_x_forwarded_host",
  244. help=_("Enable X-Forwarded-Host header if the load balancer requires it."),
  245. type=coerce_bool,
  246. default=False)
  247. SECURE_PROXY_SSL_HEADER = Config(
  248. key="secure_proxy_ssl_header",
  249. help=_("Support for HTTPS termination at the load-balancer level with SECURE_PROXY_SSL_HEADER."),
  250. type=coerce_bool,
  251. default=False)
  252. APP_BLACKLIST = Config(
  253. key='app_blacklist',
  254. default='',
  255. type=coerce_csv,
  256. help=_('Comma separated list of apps to not load at server startup.')
  257. )
  258. DEMO_ENABLED = Config( # Internal and Temporary
  259. key="demo_enabled",
  260. help=_("To set to true in combination when using Hue demo backend."),
  261. type=coerce_bool,
  262. private=True,
  263. default=False)
  264. LOG_REDACTION_FILE = Config(
  265. key="log_redaction_file",
  266. help=_("Use this file to parse and redact log message."),
  267. type=parse_redaction_policy_from_file,
  268. default=None)
  269. ALLOWED_HOSTS = Config(
  270. key='allowed_hosts',
  271. default=['*'],
  272. type=coerce_csv,
  273. help=_('Comma separated list of strings representing the host/domain names that the Hue server can serve.')
  274. )
  275. def is_https_enabled():
  276. return bool(SSL_CERTIFICATE.get() and SSL_PRIVATE_KEY.get())
  277. #
  278. # Email (SMTP) settings
  279. #
  280. _default_from_email = None
  281. def default_from_email():
  282. """Email for hue@<host-fqdn>"""
  283. global _default_from_email
  284. if _default_from_email is None:
  285. try:
  286. fqdn = socket.getfqdn()
  287. except IOError:
  288. fqdn = 'localhost'
  289. _default_from_email = "hue@" + fqdn
  290. return _default_from_email
  291. def default_database_options():
  292. """Database type dependent options"""
  293. if DATABASE.ENGINE.get().endswith('oracle'):
  294. return {'threaded': True}
  295. elif DATABASE.ENGINE.get().endswith('sqlite3'):
  296. return {'timeout': 30}
  297. else:
  298. return {}
  299. def default_secure_cookie():
  300. """Enable secure cookies if HTTPS is enabled."""
  301. return is_https_enabled()
  302. def default_ssl_cacerts():
  303. """Path to default Certificate Authority certificates"""
  304. return SSL_CACERTS.get()
  305. def default_ssl_validate():
  306. """Choose whether Hue should validate certificates received from the server."""
  307. return SSL_VALIDATE.get()
  308. SMTP = ConfigSection(
  309. key='smtp',
  310. help=_('Configuration options for connecting to an external SMTP server.'),
  311. members=dict(
  312. HOST = Config(
  313. key="host",
  314. help=_("The SMTP server for email notification delivery."),
  315. type=str,
  316. default="localhost"
  317. ),
  318. PORT = Config(
  319. key="port",
  320. help=_("The SMTP server port."),
  321. type=int,
  322. default=25
  323. ),
  324. USER = Config(
  325. key="user",
  326. help=_("The username for the SMTP host."),
  327. type=str,
  328. default=""
  329. ),
  330. PASSWORD = Config(
  331. key="password",
  332. help=_("The password for the SMTP user."),
  333. type=str,
  334. private=True,
  335. default="",
  336. ),
  337. PASSWORD_SCRIPT = Config(
  338. key="password_script",
  339. help=_("Execute this script to produce the SMTP user password. This will be used when the SMTP `password` is not set."),
  340. type=coerce_password_from_script,
  341. private=True,
  342. default="",
  343. ),
  344. USE_TLS = Config(
  345. key="tls",
  346. help=_("Whether to use a TLS (secure) connection when talking to the SMTP server."),
  347. type=coerce_bool,
  348. default=False
  349. ),
  350. DEFAULT_FROM= Config(
  351. key="default_from_email",
  352. help=_("Default email address to use for various automated notifications from Hue."),
  353. type=str,
  354. dynamic_default=default_from_email
  355. ),
  356. )
  357. )
  358. METRICS = ConfigSection(
  359. key='metrics',
  360. help=_("""Configuration options for metrics"""),
  361. members=dict(
  362. ENABLE_WEB_METRICS=Config(
  363. key='enable_web_metrics',
  364. help=_('Enable metrics URL "desktop/metrics"'),
  365. default=True,
  366. type=coerce_bool),
  367. LOCATION=Config(
  368. key='location',
  369. help=_('If specified, Hue will write metrics to this file'),
  370. type=str),
  371. COLLECTION_INTERVAL=Config(
  372. key='collection_interval',
  373. help=_('Time in milliseconds on how frequently to collect metrics'),
  374. type=coerce_positive_integer,
  375. default=30000),
  376. )
  377. )
  378. DATABASE = ConfigSection(
  379. key='database',
  380. help=_("""Configuration options for specifying the Desktop Database.
  381. For more info, see http://docs.djangoproject.com/en/1.4/ref/settings/#database-engine"""),
  382. members=dict(
  383. ENGINE=Config(
  384. key='engine',
  385. help=_('Database engine, such as postgresql_psycopg2, mysql, or sqlite3.'),
  386. type=coerce_database,
  387. default='django.db.backends.sqlite3',
  388. ),
  389. NAME=Config(
  390. key='name',
  391. help=_('Database name, or path to DB if using sqlite3.'),
  392. type=str,
  393. default=get_desktop_root('desktop.db'),
  394. ),
  395. USER=Config(
  396. key='user',
  397. help=_('Database username.'),
  398. type=str,
  399. default='',
  400. ),
  401. PASSWORD=Config(
  402. key='password',
  403. help=_('Database password.'),
  404. private=True,
  405. type=str,
  406. default="",
  407. ),
  408. PASSWORD_SCRIPT=Config(
  409. key='password_script',
  410. help=_('Execute this script to produce the database password. This will be used when `password` is not set.'),
  411. private=True,
  412. type=coerce_password_from_script,
  413. default="",
  414. ),
  415. HOST=Config(
  416. key='host',
  417. help=_('Database host.'),
  418. type=str,
  419. default='',
  420. ),
  421. PORT=Config(
  422. key='port',
  423. help=_('Database port.'),
  424. type=coerce_port,
  425. default='0',
  426. ),
  427. OPTIONS=Config(
  428. key='options',
  429. help=_('Database options to send to the server when connecting.'),
  430. type=coerce_json_dict,
  431. dynamic_default=default_database_options
  432. )
  433. )
  434. )
  435. SESSION = ConfigSection(
  436. key='session',
  437. help=_("""Configuration options for specifying the Desktop session.
  438. For more info, see https://docs.djangoproject.com/en/1.4/topics/http/sessions/"""),
  439. members=dict(
  440. TTL=Config(
  441. key='ttl',
  442. help=_("The cookie containing the users' session ID will expire after this amount of time in seconds."),
  443. type=int,
  444. default=60*60*24*14,
  445. ),
  446. SECURE=Config(
  447. key='secure',
  448. help=_("The cookie containing the users' session ID will be secure. This should only be enabled with HTTPS."),
  449. type=coerce_bool,
  450. dynamic_default=default_secure_cookie,
  451. ),
  452. HTTP_ONLY=Config(
  453. key='http_only',
  454. help=_("The cookie containing the users' session ID will use the HTTP only flag."),
  455. type=coerce_bool,
  456. default=True,
  457. ),
  458. EXPIRE_AT_BROWSER_CLOSE=Config(
  459. key='expire_at_browser_close',
  460. help=_("Use session-length cookies. Logs out the user when she closes the browser window."),
  461. type=coerce_bool,
  462. default=False
  463. )
  464. )
  465. )
  466. KERBEROS = ConfigSection(
  467. key="kerberos",
  468. help=_("""Configuration options for specifying Hue's Kerberos integration for
  469. secured Hadoop clusters."""),
  470. members=dict(
  471. HUE_KEYTAB=Config(
  472. key='hue_keytab',
  473. help=_("Path to a Kerberos keytab file containing Hue's service credentials."),
  474. type=str,
  475. default=None),
  476. HUE_PRINCIPAL=Config(
  477. key='hue_principal',
  478. help=_("Kerberos principal name for Hue. Typically 'hue/hostname.foo.com'."),
  479. type=str,
  480. default="hue/%s" % socket.getfqdn()),
  481. KEYTAB_REINIT_FREQUENCY=Config(
  482. key='reinit_frequency',
  483. help=_("Frequency in seconds with which Hue will renew its keytab."),
  484. type=int,
  485. default=60*60), #1h
  486. CCACHE_PATH=Config(
  487. key='ccache_path',
  488. help=_("Path to keep Kerberos credentials cached."),
  489. private=True,
  490. type=str,
  491. default="/tmp/hue_krb5_ccache",
  492. ),
  493. KINIT_PATH=Config(
  494. key='kinit_path',
  495. help=_("Path to Kerberos 'kinit' command."),
  496. type=str,
  497. default="kinit", # use PATH!
  498. )
  499. )
  500. )
  501. # See python's documentation for time.tzset for valid values.
  502. TIME_ZONE = Config(
  503. key="time_zone",
  504. help=_("Time zone name."),
  505. type=str,
  506. default=os.environ.get("TZ", "America/Los_Angeles")
  507. )
  508. DEFAULT_SITE_ENCODING = Config(
  509. key='default_site_encoding',
  510. help=_('Default system-wide unicode encoding.'),
  511. type=str,
  512. default='utf-8'
  513. )
  514. SERVER_USER = Config(
  515. key="server_user",
  516. help=_("Username to run servers as."),
  517. type=str,
  518. default="hue")
  519. SERVER_GROUP = Config(
  520. key="server_group",
  521. help=_("Group to run servers as."),
  522. type=str,
  523. default="hue")
  524. DEFAULT_USER = Config(
  525. key="default_user",
  526. help=_("This should be the user running hue webserver"),
  527. type=str,
  528. default="hue")
  529. DEFAULT_HDFS_SUPERUSER = Config(
  530. key="default_hdfs_superuser",
  531. help=_("This should be the hdfs super user"),
  532. type=str,
  533. default="hdfs")
  534. CUSTOM = ConfigSection(
  535. key="custom",
  536. help=_("Customizations to the UI."),
  537. members=dict(
  538. BANNER_TOP_HTML=Config("banner_top_html",
  539. default="",
  540. help=_("Top banner HTML code. This code will be placed in the navigation bar "
  541. "so that it will reside at the top of the page in a fixed position. " +
  542. "One common value is `<img src=\"http://www.example.com/example.gif\" />`")),
  543. LOGIN_SPLASH_HTML=Config("login_splash_html",
  544. default="",
  545. help=_("The login splash HTML code. This code will be placed in the login page, "
  546. "useful for security warning messages.")),
  547. ))
  548. AUTH = ConfigSection(
  549. key="auth",
  550. help=_("Configuration options for user authentication into the web application."),
  551. members=dict(
  552. BACKEND=Config("backend",
  553. default=["desktop.auth.backend.AllowFirstUserDjangoBackend"],
  554. type=coerce_csv,
  555. help=_("Authentication backend. Common settings are "
  556. "django.contrib.auth.backends.ModelBackend (fully Django backend), " +
  557. "desktop.auth.backend.AllowAllBackend (allows everyone), " +
  558. "desktop.auth.backend.AllowFirstUserDjangoBackend (relies on Django and user manager, after the first login). " +
  559. "Multiple Authentication backends are supported by specifying a comma-separated list in order of priority.")),
  560. USER_AUGMENTOR=Config("user_augmentor",
  561. default="desktop.auth.backend.DefaultUserAugmentor",
  562. help=_("Class which defines extra accessor methods for User objects.")),
  563. PAM_SERVICE=Config("pam_service",
  564. default="login",
  565. help=_("The service to use when querying PAM. "
  566. "The service usually corresponds to a single filename in /etc/pam.d")),
  567. REMOTE_USER_HEADER=Config("remote_user_header",
  568. default="HTTP_REMOTE_USER",
  569. help=_("When using the desktop.auth.backend.RemoteUserDjangoBackend, this sets "
  570. "the normalized name of the header that contains the remote user. "
  571. "The HTTP header in the request is converted to a key by converting "
  572. "all characters to uppercase, replacing any hyphens with underscores "
  573. "and adding an HTTP_ prefix to the name. So, for example, if the header "
  574. "is called Remote-User that would be configured as HTTP_REMOTE_USER")),
  575. IGNORE_USERNAME_CASE = Config("ignore_username_case",
  576. help=_("Ignore the case of usernames when searching for existing users in Hue."),
  577. type=coerce_bool,
  578. default=True),
  579. FORCE_USERNAME_LOWERCASE = Config("force_username_lowercase",
  580. help=_("Force usernames to lowercase when creating new users from LDAP."),
  581. type=coerce_bool,
  582. default=True),
  583. EXPIRES_AFTER = Config("expires_after",
  584. help=_("Users will expire after they have not logged in for 'n' amount of seconds."
  585. "A negative number means that users will never expire."),
  586. type=int,
  587. default=-1),
  588. EXPIRE_SUPERUSERS = Config("expire_superusers",
  589. help=_("Apply 'expires_after' to superusers."),
  590. type=coerce_bool,
  591. default=True),
  592. CHANGE_DEFAULT_PASSWORD = Config(
  593. key="change_default_password",
  594. help=_("When set to true this will allow you to specify a password for "
  595. "the user when you create the user and then force them to change "
  596. "their password upon first login. The default is false."),
  597. type=coerce_bool,
  598. default=False,
  599. ),
  600. LOGIN_FAILURE_LIMIT = Config(
  601. key="login_failure_limit",
  602. help=_("Number of login attempts allowed before a record is created for failed logins"),
  603. type=int,
  604. default=3,
  605. ),
  606. LOGIN_LOCK_OUT_AT_FAILURE = Config(
  607. key="login_lock_out_at_failure",
  608. help=_("After number of allowed login attempts are exceeded, do we lock out this IP and optionally user agent?"),
  609. type=coerce_bool,
  610. default=False,
  611. ),
  612. LOGIN_COOLOFF_TIME = Config(
  613. key="login_cooloff_time",
  614. help=_("If set, defines period of inactivity in seconds after which failed logins will be forgotten"),
  615. type=coerce_timedelta,
  616. default=None,
  617. ),
  618. LOGIN_LOCK_OUT_BY_COMBINATION_BROWSER_USER_AGENT_AND_IP = Config(
  619. key="login_lock_out_by_combination_browser_user_agent_and_ip",
  620. help=_("If True, lock out based on IP and browser user agent"),
  621. type=coerce_bool,
  622. default=False,
  623. ),
  624. LOGIN_LOCK_OUT_BY_COMBINATION_USER_AND_IP = Config(
  625. key="login_lock_out_by_combination_user_and_ip",
  626. help=_("If True, lock out based on IP and user"),
  627. type=coerce_bool,
  628. default=False,
  629. ),
  630. ))
  631. LDAP = ConfigSection(
  632. key="ldap",
  633. help=_("Configuration options for LDAP connectivity."),
  634. members=dict(
  635. CREATE_USERS_ON_LOGIN = Config("create_users_on_login",
  636. help=_("Create users when they login with their LDAP credentials."),
  637. type=coerce_bool,
  638. default=True),
  639. SYNC_GROUPS_ON_LOGIN = Config("sync_groups_on_login",
  640. help=_("Synchronize a users groups when they login."),
  641. type=coerce_bool,
  642. default=False),
  643. IGNORE_USERNAME_CASE = Config("ignore_username_case",
  644. help=_("Ignore the case of usernames when searching for existing users in Hue."),
  645. type=coerce_bool,
  646. default=True),
  647. FORCE_USERNAME_LOWERCASE = Config("force_username_lowercase",
  648. help=_("Force usernames to lowercase when creating new users from LDAP."),
  649. type=coerce_bool,
  650. default=True),
  651. SUBGROUPS = Config("subgroups",
  652. help=_("Choose which kind of subgrouping to use: nested or suboordinate (deprecated)."),
  653. type=coerce_str_lowercase,
  654. default="suboordinate"),
  655. NESTED_MEMBERS_SEARCH_DEPTH = Config("nested_members_search_depth",
  656. help=_("Define the number of levels to search for nested members."),
  657. type=int,
  658. default=10),
  659. FOLLOW_REFERRALS = Config("follow_referrals",
  660. help=_("Whether or not to follow referrals."),
  661. type=coerce_bool,
  662. default=False),
  663. DEBUG = Config("debug",
  664. type=coerce_bool,
  665. default=False,
  666. help=_("Set to a value to enable python-ldap debugging.")),
  667. DEBUG_LEVEL = Config("debug_level",
  668. default=255,
  669. type=int,
  670. help=_("Sets the debug level within the underlying LDAP C lib.")),
  671. TRACE_LEVEL = Config("trace_level",
  672. default=0,
  673. type=int,
  674. help=_("Possible values for trace_level are 0 for no logging, 1 for only logging the method calls with arguments,"
  675. "2 for logging the method calls with arguments and the complete results and 9 for also logging the traceback of method calls.")),
  676. LDAP_SERVERS = UnspecifiedConfigSection(
  677. key="ldap_servers",
  678. help=_("LDAP server record."),
  679. each=ConfigSection(
  680. members=dict(
  681. BASE_DN=Config("base_dn",
  682. default=None,
  683. help=_("The base LDAP distinguished name to use for LDAP search.")),
  684. NT_DOMAIN=Config("nt_domain",
  685. default=None,
  686. help=_("The NT domain used for LDAP authentication.")),
  687. LDAP_URL=Config("ldap_url",
  688. default=None,
  689. help=_("The LDAP URL to connect to.")),
  690. USE_START_TLS=Config("use_start_tls",
  691. default=True,
  692. type=coerce_bool,
  693. help=_("Use StartTLS when communicating with LDAP server.")),
  694. LDAP_CERT=Config("ldap_cert",
  695. default=None,
  696. help=_("A PEM-format file containing certificates for the CA's that Hue will trust for authentication over TLS. The certificate for the CA that signed the LDAP server certificate must be included among these certificates. See more here http://www.openldap.org/doc/admin24/tls.html.")),
  697. LDAP_USERNAME_PATTERN=Config("ldap_username_pattern",
  698. default=None,
  699. help=_("A pattern to use for constructing LDAP usernames.")),
  700. BIND_DN=Config("bind_dn",
  701. default=None,
  702. help=_("The distinguished name to bind as, when importing from LDAP.")),
  703. BIND_PASSWORD=Config("bind_password",
  704. default=None,
  705. private=True,
  706. help=_("The password for the bind user.")),
  707. BIND_PASSWORD_SCRIPT=Config("bind_password_script",
  708. default=None,
  709. private=True,
  710. type=coerce_password_from_script,
  711. help=_("Execute this script to produce the LDAP bind user password. This will be used when `bind_password` is not set.")),
  712. SEARCH_BIND_AUTHENTICATION=Config("search_bind_authentication",
  713. default=True,
  714. type=coerce_bool,
  715. help=_("Use search bind authentication.")),
  716. FOLLOW_REFERRALS = Config("follow_referrals",
  717. help=_("Whether or not to follow referrals."),
  718. type=coerce_bool,
  719. default=False),
  720. DEBUG = Config("debug",
  721. type=coerce_bool,
  722. default=False,
  723. help=_("Set to a value to enable python-ldap debugging.")),
  724. DEBUG_LEVEL = Config("debug_level",
  725. default=255,
  726. type=int,
  727. help=_("Sets the debug level within the underlying LDAP C lib.")),
  728. TRACE_LEVEL = Config("trace_level",
  729. default=0,
  730. type=int,
  731. help=_("Possible values for trace_level are 0 for no logging, 1 for only logging the method calls with arguments,"
  732. "2 for logging the method calls with arguments and the complete results and 9 for also logging the traceback of method calls.")),
  733. USERS = ConfigSection(
  734. key="users",
  735. help=_("Configuration for LDAP user schema and search."),
  736. members=dict(
  737. USER_FILTER=Config("user_filter",
  738. default="objectclass=*",
  739. help=_("A base filter for use when searching for users.")),
  740. USER_NAME_ATTR=Config("user_name_attr",
  741. default="sAMAccountName",
  742. help=_("The username attribute in the LDAP schema. "
  743. "Typically, this is 'sAMAccountName' for AD and 'uid' "
  744. "for other LDAP systems.")),
  745. )
  746. ),
  747. GROUPS = ConfigSection(
  748. key="groups",
  749. help=_("Configuration for LDAP group schema and search."),
  750. members=dict(
  751. GROUP_FILTER=Config("group_filter",
  752. default="objectclass=*",
  753. help=_("A base filter for use when searching for groups.")),
  754. GROUP_NAME_ATTR=Config("group_name_attr",
  755. default="cn",
  756. help=_("The group name attribute in the LDAP schema. "
  757. "Typically, this is 'cn'.")),
  758. GROUP_MEMBER_ATTR=Config("group_member_attr",
  759. default="member",
  760. help=_("The LDAP attribute which specifies the "
  761. "members of a group.")),
  762. ))))),
  763. # Every thing below here is deprecated and should be removed in an upcoming major release.
  764. BASE_DN=Config("base_dn",
  765. default=None,
  766. help=_("The base LDAP distinguished name to use for LDAP search.")),
  767. NT_DOMAIN=Config("nt_domain",
  768. default=None,
  769. help=_("The NT domain used for LDAP authentication.")),
  770. LDAP_URL=Config("ldap_url",
  771. default=None,
  772. help=_("The LDAP URL to connect to.")),
  773. USE_START_TLS=Config("use_start_tls",
  774. default=True,
  775. type=coerce_bool,
  776. help=_("Use StartTLS when communicating with LDAP server.")),
  777. LDAP_CERT=Config("ldap_cert",
  778. default=None,
  779. help=_("A PEM-format file containing certificates for the CA's that Hue will trust for authentication over TLS. The certificate for the CA that signed the LDAP server certificate must be included among these certificates. See more here http://www.openldap.org/doc/admin24/tls.html.")),
  780. LDAP_USERNAME_PATTERN=Config("ldap_username_pattern",
  781. default=None,
  782. help=_("A pattern to use for constructing LDAP usernames.")),
  783. BIND_DN=Config("bind_dn",
  784. default=None,
  785. help=_("The distinguished name to bind as, when importing from LDAP.")),
  786. BIND_PASSWORD=Config("bind_password",
  787. default=None,
  788. private=True,
  789. help=_("The password for the bind user.")),
  790. BIND_PASSWORD_SCRIPT=Config("bind_password_script",
  791. default=None,
  792. private=True,
  793. type=coerce_password_from_script,
  794. help=_("Execute this script to produce the LDAP bind user password. This will be used when `bind_password` is not set.")),
  795. SEARCH_BIND_AUTHENTICATION=Config("search_bind_authentication",
  796. default=True,
  797. type=coerce_bool,
  798. help=_("Use search bind authentication.")),
  799. USERS = ConfigSection(
  800. key="users",
  801. help=_("Configuration for LDAP user schema and search."),
  802. members=dict(
  803. USER_FILTER=Config("user_filter",
  804. default="objectclass=*",
  805. help=_("A base filter for use when searching for users.")),
  806. USER_NAME_ATTR=Config("user_name_attr",
  807. default="sAMAccountName",
  808. help=_("The username attribute in the LDAP schema. "
  809. "Typically, this is 'sAMAccountName' for AD and 'uid' "
  810. "for other LDAP systems.")),
  811. )),
  812. GROUPS = ConfigSection(
  813. key="groups",
  814. help=_("Configuration for LDAP group schema and search."),
  815. members=dict(
  816. GROUP_FILTER=Config("group_filter",
  817. default="objectclass=*",
  818. help=_("A base filter for use when searching for groups.")),
  819. GROUP_NAME_ATTR=Config("group_name_attr",
  820. default="cn",
  821. help=_("The group name attribute in the LDAP schema. "
  822. "Typically, this is 'cn'.")),
  823. GROUP_MEMBER_ATTR=Config("group_member_attr",
  824. default="member",
  825. help=_("The LDAP attribute which specifies the "
  826. "members of a group.")),
  827. ))))
  828. OAUTH = ConfigSection(
  829. key='oauth',
  830. help=_('Configuration options for Oauth 1.0 authentication'),
  831. members=dict(
  832. CONSUMER_KEY = Config(
  833. key="consumer_key",
  834. help=_("The Consumer key of the application."),
  835. type=str,
  836. default="XXXXXXXXXXXXXXXXXXXXX"
  837. ),
  838. CONSUMER_SECRET = Config(
  839. key="consumer_secret",
  840. help=_("The Consumer secret of the application."),
  841. type=str,
  842. default="XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
  843. ),
  844. REQUEST_TOKEN_URL = Config(
  845. key="request_token_url",
  846. help=_("The Request token URL."),
  847. type=str,
  848. default="https://api.twitter.com/oauth/request_token"
  849. ),
  850. ACCESS_TOKEN_URL = Config(
  851. key="access_token_url",
  852. help=_("The Access token URL."),
  853. type=str,
  854. default="https://api.twitter.com/oauth/access_token"
  855. ),
  856. AUTHENTICATE_URL = Config(
  857. key="authenticate_url",
  858. help=_("The Authorize URL."),
  859. type=str,
  860. default="https://api.twitter.com/oauth/authorize"
  861. ),
  862. )
  863. )
  864. LOCAL_FILESYSTEMS = UnspecifiedConfigSection(
  865. key="local_filesystems",
  866. help=_("Paths on the local file system that users should be able to browse."),
  867. each=ConfigSection(
  868. members=dict(
  869. PATH=Config("path",
  870. required=True,
  871. help=_("The path on the local filesystem.")))))
  872. def default_feedback_url():
  873. """A version-specific URL."""
  874. return "http://groups.google.com/a/cloudera.org/group/hue-user"
  875. FEEDBACK_URL = Config(
  876. key="feedback_url",
  877. help=_("Link for 'feedback' tab."),
  878. type=str,
  879. dynamic_default=default_feedback_url
  880. )
  881. SEND_DBUG_MESSAGES = Config(
  882. key="send_dbug_messages",
  883. help=_("Whether to send debug messages from JavaScript to the server logs."),
  884. type=coerce_bool,
  885. default=False
  886. )
  887. DATABASE_LOGGING = Config(
  888. key="database_logging",
  889. help=_("Enable or disable database debug mode."),
  890. type=coerce_bool,
  891. default=False
  892. )
  893. DJANGO_ADMINS = UnspecifiedConfigSection(
  894. key="django_admins",
  895. help=_("Administrators that should receive error emails."),
  896. each=ConfigSection(
  897. members=dict(
  898. NAME=Config("name",
  899. required=True,
  900. help=_("The full name of the admin.")),
  901. EMAIL=Config("email",
  902. required=True,
  903. help=_("The email address of the admin.")))))
  904. DJANGO_DEBUG_MODE = Config(
  905. key="django_debug_mode",
  906. help=_("Enable or disable Django debug mode."),
  907. type=coerce_bool,
  908. default=True
  909. )
  910. HTTP_500_DEBUG_MODE = Config(
  911. key='http_500_debug_mode',
  912. help=_('Enable or disable debugging information in the 500 internal server error response. '
  913. 'Note that the debugging information may contain sensitive data. '
  914. 'If django_debug_mode is True, this is automatically enabled.'),
  915. type=coerce_bool,
  916. default=True
  917. )
  918. MEMORY_PROFILER = Config(
  919. key='memory_profiler',
  920. help=_('Enable or disable memory profiling.'),
  921. type=coerce_bool,
  922. default=False)
  923. AUDIT_EVENT_LOG_DIR = Config(
  924. key="audit_event_log_dir",
  925. help=_("The directory where to store the auditing logs. Auditing is disable if the value is empty."),
  926. type=str,
  927. default=""
  928. )
  929. AUDIT_LOG_MAX_FILE_SIZE = Config(
  930. key="audit_log_max_file_size",
  931. help=_("Size in KB/MB/GB for audit log to rollover."),
  932. type=str,
  933. default="100MB"
  934. )
  935. DJANGO_SERVER_EMAIL = Config(
  936. key='django_server_email',
  937. help=_('Email address that internal error messages should send as.'),
  938. default='hue@localhost.localdomain'
  939. )
  940. DJANGO_EMAIL_BACKEND = Config(
  941. key="django_email_backend",
  942. help=_("The email backend to use."),
  943. type=str,
  944. default="django.core.mail.backends.smtp.EmailBackend"
  945. )
  946. def validate_ldap(user, config):
  947. res = []
  948. if config.SEARCH_BIND_AUTHENTICATION.get():
  949. if config.LDAP_URL.get() is not None:
  950. bind_dn = config.BIND_DN.get()
  951. bind_password = get_ldap_bind_password(config)
  952. if bool(bind_dn) != bool(bind_password):
  953. if bind_dn == None:
  954. res.append((LDAP.BIND_DN,
  955. unicode(_("If you set bind_password, then you must set bind_dn."))))
  956. else:
  957. res.append((LDAP.BIND_PASSWORD,
  958. unicode(_("If you set bind_dn, then you must set bind_password."))))
  959. else:
  960. if config.NT_DOMAIN.get() is not None or \
  961. config.LDAP_USERNAME_PATTERN.get() is not None:
  962. if config.LDAP_URL.get() is None:
  963. res.append((config.LDAP_URL,
  964. unicode(_("LDAP is only partially configured. An LDAP URL must be provided."))))
  965. if config.LDAP_URL.get() is not None:
  966. if config.NT_DOMAIN.get() is None and \
  967. config.LDAP_USERNAME_PATTERN.get() is None:
  968. res.append((config.LDAP_URL,
  969. unicode(_("LDAP is only partially configured. An NT Domain or username "
  970. "search pattern must be provided."))))
  971. if config.LDAP_USERNAME_PATTERN.get() is not None and \
  972. '<username>' not in config.LDAP_USERNAME_PATTERN.get():
  973. res.append((config.LDAP_USERNAME_PATTERN,
  974. unicode(_("The LDAP username pattern should contain the special"
  975. "<username> replacement string for authentication."))))
  976. return res
  977. def validate_database():
  978. from django.db import connection
  979. LOG = logging.getLogger(__name__)
  980. res = []
  981. if connection.vendor == 'mysql':
  982. cursor = connection.cursor();
  983. try:
  984. innodb_table_count = cursor.execute('''
  985. SELECT *
  986. FROM information_schema.tables
  987. WHERE table_schema=DATABASE() AND engine = "innodb"''')
  988. total_table_count = cursor.execute('''
  989. SELECT *
  990. FROM information_schema.tables
  991. WHERE table_schema=DATABASE()''')
  992. # Promote InnoDB storage engine
  993. if innodb_table_count != total_table_count:
  994. res.append(('PREFERRED_STORAGE_ENGINE', unicode(_('''We recommend MySQL InnoDB engine over
  995. MyISAM which does not support transactions.'''))))
  996. if innodb_table_count != 0 and innodb_table_count != total_table_count:
  997. res.append(('MYSQL_STORAGE_ENGINE', unicode(_('''All tables in the database must be of the same
  998. storage engine type (preferably InnoDB).'''))))
  999. except Exception, ex:
  1000. LOG.exception("Error in config validation of MYSQL_STORAGE_ENGINE: %s", ex)
  1001. elif 'sqlite' in connection.vendor:
  1002. res.append(('SQLITE_NOT_FOR_PRODUCTION_USE', unicode(_('SQLite is only recommended for small development environments with a few users.'))))
  1003. return res
  1004. def config_validator(user):
  1005. """
  1006. config_validator() -> [ (config_variable, error_message) ]
  1007. Called by core check_config() view.
  1008. """
  1009. from desktop.lib import i18n
  1010. res = []
  1011. if not get_secret_key():
  1012. res.append((SECRET_KEY, unicode(_("Secret key should be configured as a random string. All sessions will be lost on restart"))))
  1013. # Validate SSL setup
  1014. if SSL_CERTIFICATE.get():
  1015. res.extend(validate_path(SSL_CERTIFICATE, is_dir=False))
  1016. if not SSL_PRIVATE_KEY.get():
  1017. res.append((SSL_PRIVATE_KEY, unicode(_("SSL private key file should be set to enable HTTPS."))))
  1018. else:
  1019. res.extend(validate_path(SSL_PRIVATE_KEY, is_dir=False))
  1020. # Validate encoding
  1021. if not i18n.validate_encoding(DEFAULT_SITE_ENCODING.get()):
  1022. res.append((DEFAULT_SITE_ENCODING, unicode(_("Encoding not supported."))))
  1023. # Validate kerberos
  1024. if KERBEROS.HUE_KEYTAB.get() is not None:
  1025. res.extend(validate_path(KERBEROS.HUE_KEYTAB, is_dir=False))
  1026. # Keytab should not be world or group accessible
  1027. kt_stat = os.stat(KERBEROS.HUE_KEYTAB.get())
  1028. if stat.S_IMODE(kt_stat.st_mode) & 0077:
  1029. res.append((KERBEROS.HUE_KEYTAB,
  1030. force_unicode(_("Keytab should have 0600 permissions (has %o).") %
  1031. stat.S_IMODE(kt_stat.st_mode))))
  1032. res.extend(validate_path(KERBEROS.KINIT_PATH, is_dir=False))
  1033. res.extend(validate_path(KERBEROS.CCACHE_PATH, is_dir=False))
  1034. if LDAP.LDAP_SERVERS.get():
  1035. for ldap_record_key in LDAP.LDAP_SERVERS.get():
  1036. res.extend(validate_ldap(user, LDAP.LDAP_SERVERS.get()[ldap_record_key]))
  1037. else:
  1038. res.extend(validate_ldap(user, LDAP))
  1039. # Validate MYSQL storage engine of all tables
  1040. res.extend(validate_database())
  1041. return res
  1042. def get_redaction_policy():
  1043. """
  1044. Return the configured redaction policy.
  1045. """
  1046. return LOG_REDACTION_FILE.get()
  1047. def get_secret_key():
  1048. secret_key = os.environ.get('HUE_SECRET_KEY')
  1049. if secret_key is not None:
  1050. return secret_key
  1051. secret_key = SECRET_KEY.get()
  1052. if not secret_key:
  1053. secret_key = SECRET_KEY_SCRIPT.get()
  1054. return secret_key
  1055. def get_ssl_password():
  1056. password = os.environ.get('HUE_SSL_PASSWORD')
  1057. if password is not None:
  1058. return password
  1059. password = SSL_PASSWORD.get()
  1060. if not password:
  1061. password = SSL_PASSWORD_SCRIPT.get()
  1062. return password
  1063. def get_database_password():
  1064. password = os.environ.get('HUE_DATABASE_PASSWORD')
  1065. if password is not None:
  1066. return password
  1067. password = DATABASE.PASSWORD.get()
  1068. if not password:
  1069. password = DATABASE.PASSWORD_SCRIPT.get()
  1070. return password
  1071. def get_smtp_password():
  1072. password = os.environ.get('HUE_SMTP_PASSWORD')
  1073. if password is not None:
  1074. return password
  1075. password = SMTP.PASSWORD.get()
  1076. if not password:
  1077. password = SMTP.PASSWORD_SCRIPT.get()
  1078. return password
  1079. def get_ldap_bind_password(ldap_config):
  1080. password = os.environ.get('HUE_LDAP_BIND_PASSWORD')
  1081. if password is not None:
  1082. return password
  1083. password = ldap_config.BIND_PASSWORD.get()
  1084. if not password:
  1085. password = ldap_config.BIND_PASSWORD_SCRIPT.get()
  1086. return password