conf.py 74 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169
  1. #!/usr/bin/env python
  2. ## -*- coding: utf-8 -*-
  3. # Licensed to Cloudera, Inc. under one
  4. # or more contributor license agreements. See the NOTICE file
  5. # distributed with this work for additional information
  6. # regarding copyright ownership. Cloudera, Inc. licenses this file
  7. # to you under the Apache License, Version 2.0 (the
  8. # "License"); you may not use this file except in compliance
  9. # with the License. You may obtain a copy of the License at
  10. #
  11. # http://www.apache.org/licenses/LICENSE-2.0
  12. #
  13. # Unless required by applicable law or agreed to in writing, software
  14. # distributed under the License is distributed on an "AS IS" BASIS,
  15. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  16. # See the License for the specific language governing permissions and
  17. # limitations under the License.
  18. import datetime
  19. import glob
  20. import logging
  21. import os
  22. import socket
  23. import stat
  24. import sys
  25. from collections import OrderedDict
  26. from django.db import connection
  27. from django.utils.translation import ugettext_lazy as _
  28. from metadata.metadata_sites import get_navigator_audit_log_dir, get_navigator_audit_max_file_size
  29. from desktop import appmanager
  30. from desktop.redaction.engine import parse_redaction_policy_from_file
  31. from desktop.lib.conf import Config, ConfigSection, UnspecifiedConfigSection,\
  32. coerce_bool, coerce_csv, coerce_json_dict,\
  33. validate_path, list_of_compiled_res, coerce_str_lowercase, \
  34. coerce_password_from_script, coerce_string
  35. from desktop.lib.i18n import force_unicode
  36. from desktop.lib.paths import get_desktop_root, get_run_root
  37. if sys.version_info[0] > 2:
  38. from builtins import str as new_str
  39. else:
  40. new_str = unicode
  41. LOG = logging.getLogger(__name__)
  42. def is_oozie_enabled():
  43. """Oozie needs to be available as it is the backend."""
  44. return len([app for app in appmanager.DESKTOP_MODULES if app.name == 'oozie']) > 0
  45. def coerce_database(database):
  46. if database == 'mysql':
  47. return 'django.db.backends.mysql'
  48. elif database == 'postgres' or database == 'postgresql_psycopg2':
  49. return 'django.db.backends.postgresql_psycopg2'
  50. elif database == 'oracle':
  51. return 'django.db.backends.oracle'
  52. elif database in ('sqlite', 'sqlite3'):
  53. return 'django.db.backends.sqlite3'
  54. else:
  55. return str(database)
  56. def coerce_port(port):
  57. try:
  58. port = int(port)
  59. if port == 0:
  60. port = ''
  61. except ValueError:
  62. port = ''
  63. return port
  64. def coerce_file(path):
  65. if path and not os.path.isfile(path):
  66. raise Exception('File %s does not exist.' % path)
  67. return path
  68. def coerce_timedelta(value):
  69. return datetime.timedelta(seconds=int(value))
  70. def get_dn(fqdn=None):
  71. """This function returns fqdn(if possible)"""
  72. val = []
  73. LOG = logging.getLogger(__name__)
  74. try:
  75. if fqdn is None:
  76. fqdn = socket.getfqdn()
  77. if '.' in fqdn:
  78. tups = fqdn.split('.')
  79. if len(tups) > 2:
  80. val.append(".%s" % ('.'.join(tups[-2:])))
  81. else:
  82. LOG.warning("allowed_hosts value to '*'. It is a security risk")
  83. val.append('*')
  84. else:
  85. LOG.warning("allowed_hosts value to '*'. It is a security risk")
  86. val.append('*')
  87. except:
  88. LOG.warning("allowed_hosts value to '*'. It is a security risk")
  89. val.append('*')
  90. return val
  91. def coerce_positive_integer(integer):
  92. integer = int(integer)
  93. if integer <= 0:
  94. raise Exception('integer is not positive')
  95. return integer
  96. def is_lb_enabled():
  97. """Check for Hue Load Balancer is available"""
  98. return bool(HUE_LOAD_BALANCER.get())
  99. def coerce_zero_or_positive_integer(integer):
  100. integer = int(integer)
  101. if integer < 0:
  102. raise Exception('integer is negative')
  103. return integer
  104. def is_https_enabled():
  105. """Hue is configured for HTTPS."""
  106. return bool(SSL_CERTIFICATE.get() and SSL_PRIVATE_KEY.get())
  107. USE_CHERRYPY_SERVER = Config(
  108. key="use_cherrypy_server",
  109. help=_("If set to true, CherryPy will be used. Otherwise, Gunicorn will be used as the webserver."),
  110. type=coerce_bool,
  111. default=True)
  112. GUNICORN_WORKER_CLASS = Config(
  113. key="gunicorn_work_class",
  114. help=_("Gunicorn work class: gevent or evenlet, gthread or sync."),
  115. type=str,
  116. default="eventlet")
  117. GUNICORN_NUMBER_OF_WORKERS = Config(
  118. key="gunicorn_number_of_workers",
  119. help=_("The number of Gunicorn worker processes. If not specified, it uses: (number of CPU * 2) + 1."),
  120. type=int,
  121. default=None)
  122. HTTP_HOST = Config(
  123. key="http_host",
  124. help=_("HTTP host to bind to."),
  125. type=str,
  126. default="0.0.0.0")
  127. HTTP_PORT = Config(
  128. key="http_port",
  129. help=_("HTTP port to bind to."),
  130. type=int,
  131. default=8888)
  132. HTTP_ALLOWED_METHODS = Config(
  133. key="http_allowed_methods",
  134. help=_("HTTP methods the server will be allowed to service."),
  135. type=coerce_csv,
  136. private=True,
  137. default=['OPTIONS', 'GET', 'HEAD', 'POST', 'PUT', 'DELETE', 'CONNECT'])
  138. HUE_LOAD_BALANCER = Config(
  139. key="hue_load_balancer",
  140. help=_("A comma-separated list of available Hue load balancers."),
  141. type=coerce_csv,
  142. default=[]
  143. )
  144. X_FRAME_OPTIONS = Config(
  145. key="http_x_frame_options",
  146. help=_("X-Frame-Options HTTP header value."),
  147. type=str,
  148. default="SAMEORIGIN")
  149. SSL_CERTIFICATE = Config(
  150. key="ssl_certificate",
  151. help=_("Filename of SSL Certificate"),
  152. type=coerce_file,
  153. default=None)
  154. SSL_PRIVATE_KEY = Config(
  155. key="ssl_private_key",
  156. help=_("Filename of SSL RSA Private Key"),
  157. type=coerce_file,
  158. default=None)
  159. SSL_CERTIFICATE_CHAIN = Config(
  160. key="ssl_certificate_chain",
  161. help=_("Filename of SSL Certificate Chain"),
  162. type=coerce_file,
  163. default=None)
  164. SSL_CIPHER_LIST = Config(
  165. key="ssl_cipher_list",
  166. help=_("List of allowed and disallowed ciphers"),
  167. # From https://wiki.mozilla.org/Security/Server_Side_TLS v3.7 default
  168. # recommendation, which should be compatible with Firefox 1, Chrome 1, IE 7,
  169. # Opera 5 and Safari 1.
  170. default=':'.join([
  171. 'ECDHE-RSA-AES128-GCM-SHA256',
  172. 'ECDHE-ECDSA-AES128-GCM-SHA256',
  173. 'ECDHE-RSA-AES256-GCM-SHA384',
  174. 'ECDHE-ECDSA-AES256-GCM-SHA384',
  175. 'DHE-RSA-AES128-GCM-SHA256',
  176. 'DHE-DSS-AES128-GCM-SHA256',
  177. 'kEDH+AESGCM',
  178. 'ECDHE-RSA-AES128-SHA256',
  179. 'ECDHE-ECDSA-AES128-SHA256',
  180. 'ECDHE-RSA-AES128-SHA',
  181. 'ECDHE-ECDSA-AES128-SHA',
  182. 'ECDHE-RSA-AES256-SHA384',
  183. 'ECDHE-ECDSA-AES256-SHA384',
  184. 'ECDHE-RSA-AES256-SHA',
  185. 'ECDHE-ECDSA-AES256-SHA',
  186. 'DHE-RSA-AES128-SHA256',
  187. 'DHE-RSA-AES128-SHA',
  188. 'DHE-DSS-AES128-SHA256',
  189. 'DHE-RSA-AES256-SHA256',
  190. 'DHE-DSS-AES256-SHA',
  191. 'DHE-RSA-AES256-SHA',
  192. 'AES128-GCM-SHA256',
  193. 'AES256-GCM-SHA384',
  194. 'AES128-SHA256',
  195. 'AES256-SHA256',
  196. 'AES128-SHA',
  197. 'AES256-SHA',
  198. 'AES',
  199. 'CAMELLIA',
  200. 'DES-CBC3-SHA',
  201. '!aNULL',
  202. '!eNULL',
  203. '!EXPORT',
  204. '!DES',
  205. '!RC4',
  206. '!MD5',
  207. '!PSK',
  208. '!aECDH',
  209. '!EDH-DSS-DES-CBC3-SHA',
  210. '!EDH-RSA-DES-CBC3-SHA',
  211. '!KRB5-DES-CBC3-SHA',
  212. ]))
  213. SSL_PASSWORD = Config(
  214. key="ssl_password",
  215. help=_("SSL password of the certificate"),
  216. default=None)
  217. SSL_PASSWORD_SCRIPT = Config(
  218. key="ssl_password_script",
  219. help=_("Execute this script to produce the SSL password. This will be used when `ssl_password` is not set."),
  220. type=coerce_password_from_script,
  221. default=None)
  222. SSL_CACERTS = Config(
  223. key="ssl_cacerts",
  224. help=_('Path to default Certificate Authority certificates.'),
  225. type=str,
  226. default='')
  227. SSL_VALIDATE = Config(
  228. key="ssl_validate",
  229. help=_('Choose whether Hue should validate certificates received from the server.'),
  230. type=coerce_bool,
  231. default=True)
  232. SECURE_HSTS_SECONDS = Config(
  233. key="secure_hsts_seconds",
  234. help=_('Strict-Transport-Security: max-age=31536000 This is a HTTP response header, Once a supported browser receives this header that browser will prevent any communications from being sent over HTTP to the specified domain and will instead send all communications over HTTPS.'),
  235. type=int,
  236. default=31536000)
  237. SECURE_HSTS_INCLUDE_SUBDOMAINS = Config(
  238. key="secure_hsts_include_subdomains",
  239. help=_('Strict-Transport-Security: This is a HTTP response header, Once a supported browser receives this header that browser will prevent any communications from being sent over HTTP to the specified domain and will instead send all communications over HTTPS.'),
  240. type=coerce_bool,
  241. default=True)
  242. SECURE_CONTENT_TYPE_NOSNIFF = Config(
  243. key="secure_content_type_nosniff",
  244. help=_('X-Content-Type-Options: nosniff This is a HTTP response header feature that helps prevent attacks based on MIME-type confusion.'),
  245. type=coerce_bool,
  246. default=True)
  247. SECURE_BROWSER_XSS_FILTER = Config(
  248. key="secure_browser_xss_filter",
  249. help=_('X-Xss-Protection: \"1; mode=block\" This is a HTTP response header feature to force XSS protection.'),
  250. type=coerce_bool,
  251. default=True)
  252. SECURE_CONTENT_SECURITY_POLICY = Config(
  253. key="secure_content_security_policy",
  254. help=_('X-Content-Type-Options: nosniff This is a HTTP response header feature that helps prevent attacks based on MIME-type confusion.'),
  255. type=str,
  256. default="script-src 'self' 'unsafe-inline' 'unsafe-eval' *.google-analytics.com *.doubleclick.net data:;"+
  257. "img-src 'self' *.google-analytics.com *.doubleclick.net http://*.tile.osm.org *.tile.osm.org *.gstatic.com data:;"+
  258. "style-src 'self' 'unsafe-inline' fonts.googleapis.com;"+
  259. "connect-src 'self';"+
  260. "frame-src *;"+
  261. "child-src 'self' data: *.vimeo.com;"+
  262. "object-src 'none'")
  263. SECURE_SSL_REDIRECT = Config(
  264. key="secure_ssl_redirect",
  265. help=_('If all non-SSL requests should be permanently redirected to SSL.'),
  266. type=coerce_bool,
  267. dynamic_default=is_https_enabled)
  268. SECURE_SSL_HOST = Config(
  269. key="secure_redirect_host",
  270. help=_('If all non-SSL requests should be permanently redirected to this SSL host.'),
  271. type=str,
  272. default="0.0.0.0")
  273. SECURE_REDIRECT_EXEMPT = Config(
  274. key="secure_redirect_exempt",
  275. help=_('Comma separated list of strings representing the host/domain names that the Hue server can not serve https.'),
  276. type=coerce_csv,
  277. default=[])
  278. # Deprecated by AUTH_PASSWORD
  279. LDAP_PASSWORD = Config(
  280. key="ldap_password",
  281. help=_("LDAP password of the hue user used for LDAP authentications. For example for LDAP Authentication with HiveServer2/Impala."),
  282. private=True,
  283. default=None)
  284. # Deprecated by AUTH_PASSWORD_SCRIPT
  285. LDAP_PASSWORD_SCRIPT = Config(
  286. key="ldap_password_script",
  287. help=_("Execute this script to produce the LDAP password. This will be used when `ldap_password` is not set."),
  288. private=True,
  289. type=coerce_password_from_script,
  290. default=None)
  291. # Deprecated by by AUTH_USERNAME
  292. LDAP_USERNAME = Config(
  293. key="ldap_username",
  294. help=_("LDAP username of the hue user used for LDAP authentications. For example for LDAP Authentication with HiveServer2/Impala."),
  295. private=True,
  296. default="hue")
  297. def get_auth_username():
  298. """Backward compatibility"""
  299. return LDAP_USERNAME.get()
  300. AUTH_USERNAME = Config(
  301. key="auth_username",
  302. help=_("Auth username of the hue user used for authentications. For example for LDAP Authentication with HiveServer2/Impala."),
  303. dynamic_default=get_auth_username)
  304. def get_auth_password():
  305. """Get from script or backward compatibility"""
  306. password = os.environ.get('HUE_AUTH_PASSWORD')
  307. if password is not None:
  308. return password
  309. password = AUTH_PASSWORD_SCRIPT.get()
  310. if password:
  311. return password
  312. password = os.environ.get('HUE_LDAP_PASSWORD')
  313. if password is not None:
  314. return password
  315. password = LDAP_PASSWORD.get() # 2 levels for backward compatibility
  316. if password:
  317. return password
  318. return LDAP_PASSWORD_SCRIPT.get()
  319. AUTH_PASSWORD = Config(
  320. key="auth_password",
  321. help=_("LDAP/PAM/.. password of the hue user used for authentications. Inactive if empty. For example for LDAP Authentication with HiveServer2/Impala."),
  322. private=True,
  323. dynamic_default=get_auth_password)
  324. AUTH_PASSWORD_SCRIPT = Config(
  325. key="auth_password_script",
  326. help=_("Execute this script to produce the auth password. This will be used when `auth_password` is not set."),
  327. private=True,
  328. type=coerce_password_from_script,
  329. default=None)
  330. ENABLE_SERVER = Config(
  331. key="enable_server",
  332. help=_("If set to false, runcpserver will not actually start the web server. Used if Apache is being used as a WSGI container."),
  333. type=coerce_bool,
  334. default=True)
  335. CHERRYPY_SERVER_THREADS = Config(
  336. key="cherrypy_server_threads",
  337. help=_("Number of threads used by the CherryPy web server."),
  338. type=int,
  339. default=50)
  340. SECRET_KEY = Config(
  341. key="secret_key",
  342. help=_("Used in hashing algorithms for sessions."),
  343. default="")
  344. SECRET_KEY_SCRIPT = Config(
  345. key="secret_key_script",
  346. help=_("Execute this script to produce the Django secret key. This will be used when `secret_key` is not set."),
  347. type=coerce_password_from_script,
  348. private=True,
  349. default="")
  350. USER_ACCESS_HISTORY_SIZE = Config(
  351. key="user_access_history_size",
  352. help=_("Number of user access to remember per view per user."),
  353. type=int,
  354. default=10)
  355. COLLECT_USAGE = Config(
  356. key="collect_usage",
  357. help=_("Help improve Hue with anonymous usage analytics."
  358. "Use Google Analytics to see how many times an application or specific section of an application is used, nothing more."),
  359. type=coerce_bool,
  360. default=True)
  361. REST_RESPONSE_SIZE = Config(
  362. key="rest_response_size",
  363. help=_("Number of characters the rest api reponse calls to dump to the logs when debug is enabled. Enter -1 for entire response."),
  364. type=int,
  365. default=2000)
  366. LEAFLET_TILE_LAYER = Config(
  367. key="leaflet_tile_layer",
  368. help=_("Tile layer server URL for the Leaflet map charts. Read more on http://leafletjs.com/reference.html#tilelayer. Make sure you add the tile domain to the img-src section of the 'secure_content_security_policy' configuration parameter as well."),
  369. type=str,
  370. default="http://{s}.tile.osm.org/{z}/{x}/{y}.png")
  371. LEAFLET_TILE_LAYER_ATTRIBUTION = Config(
  372. key="leaflet_tile_layer_attribution",
  373. help=_("The copyright message for the specified Leaflet maps Tile Layer"),
  374. default='&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors')
  375. LEAFLET_MAP_OPTIONS = Config(
  376. key="leaflet_map_options",
  377. help=_("All the map options, accordingly to http://leafletjs.com/reference-0.7.7.html#map-options. To change CRS, just use the name, ie. 'EPSG4326'"),
  378. type=coerce_json_dict,
  379. default="{}")
  380. LEAFLET_TILE_LAYER_OPTIONS = Config(
  381. key="leaflet_tile_layer_options",
  382. help=_("All the tile layer options, accordingly to http://leafletjs.com/reference-0.7.7.html#tilelayer"),
  383. type=coerce_json_dict,
  384. default="{}")
  385. POLL_ENABLED = Config(
  386. key="poll_enabled",
  387. help=_("Use poll(2) in Hue thrift pool."),
  388. type=coerce_bool,
  389. private=True,
  390. default=True)
  391. MIDDLEWARE = Config(
  392. key="middleware",
  393. help=_("Comma-separated list of Django middleware classes to use. " +
  394. "See https://docs.djangoproject.com/en/1.4/ref/middleware/ for " +
  395. "more details on middlewares in Django."),
  396. type=coerce_csv,
  397. default=[])
  398. REDIRECT_WHITELIST = Config(
  399. key="redirect_whitelist",
  400. help=_("Comma-separated list of regular expressions, which match the redirect URL."
  401. "For example, to restrict to your local domain and FQDN, the following value can be used:"
  402. " ^\/.*$,^http:\/\/www.mydomain.com\/.*$"),
  403. type=list_of_compiled_res(skip_empty=True),
  404. default='^(\/[a-zA-Z0-9]+.*|\/)$')
  405. USE_X_FORWARDED_HOST = Config(
  406. key="use_x_forwarded_host",
  407. help=_("Enable X-Forwarded-Host header if the load balancer requires it."),
  408. type=coerce_bool,
  409. dynamic_default=is_lb_enabled)
  410. SECURE_PROXY_SSL_HEADER = Config(
  411. key="secure_proxy_ssl_header",
  412. help=_("Support for HTTPS termination at the load-balancer level with SECURE_PROXY_SSL_HEADER."),
  413. type=coerce_bool,
  414. default=False)
  415. APP_BLACKLIST = Config(
  416. key='app_blacklist',
  417. default='',
  418. type=coerce_csv,
  419. help=_('Comma separated list of apps to not load at server startup.')
  420. )
  421. CLUSTER_ID = Config(
  422. key="cluster_id",
  423. help=_("Id of the cluster where Hue is located."),
  424. private=False,
  425. default='default')
  426. DEMO_ENABLED = Config( # Internal and Temporary
  427. key="demo_enabled",
  428. help=_("To set to true in combination when using Hue demo backend."),
  429. type=coerce_bool,
  430. private=True,
  431. default=False)
  432. LOG_REDACTION_FILE = Config(
  433. key="log_redaction_file",
  434. help=_("Use this file to parse and redact log message."),
  435. type=parse_redaction_policy_from_file,
  436. default=None)
  437. ALLOWED_HOSTS = Config(
  438. key='allowed_hosts',
  439. # dynamic_default=get_dn, # Currently off as pretty disruptive
  440. default=['*'],
  441. type=coerce_csv,
  442. help=_('Comma separated list of strings representing the host/domain names that the Hue server can serve.')
  443. )
  444. REST_CONN_TIMEOUT = Config(
  445. key='rest_conn_timeout',
  446. default=120,
  447. type=int,
  448. help=_('Timeout in seconds for REST calls.'))
  449. VCS = UnspecifiedConfigSection(
  450. "vcs",
  451. help="One entry for each Version Control",
  452. each=ConfigSection(
  453. help="""Configuration options for source version control used to list and
  454. save files from the editor. Example: Git, SVN""",
  455. members=dict(
  456. REMOTE_URL = Config(
  457. key="remote_url",
  458. help=_("Base URL to Interface Remote Server"),
  459. default='https://github.com/cloudera/hue/tree/master',
  460. type=coerce_string,
  461. ),
  462. API_URL = Config(
  463. key="api_url",
  464. help=_("Base URL to Interface API"),
  465. default='https://api.github.com',
  466. type=coerce_string,
  467. ),
  468. CLIENT_ID = Config(
  469. key="client_id",
  470. help=_("The Client ID of the Interface application."),
  471. type=coerce_string,
  472. default=""
  473. ),
  474. CLIENT_SECRET = Config(
  475. key="client_secret",
  476. help=_("The Client Secret of the Interface application."),
  477. type=coerce_string,
  478. default=""
  479. )
  480. )
  481. )
  482. )
  483. def default_secure_cookie():
  484. """Enable secure cookies if HTTPS is enabled."""
  485. return is_https_enabled()
  486. def default_ssl_cacerts():
  487. """Path to default Certificate Authority certificates"""
  488. return SSL_CACERTS.get()
  489. def default_ssl_validate():
  490. """Choose whether Hue should validate certificates received from the server."""
  491. return SSL_VALIDATE.get()
  492. #
  493. # Email (SMTP) settings
  494. #
  495. _default_from_email = None
  496. def default_from_email():
  497. """Email for hue@<host-fqdn>"""
  498. global _default_from_email
  499. if _default_from_email is None:
  500. try:
  501. fqdn = socket.getfqdn()
  502. except IOError:
  503. fqdn = 'localhost'
  504. _default_from_email = "hue@" + fqdn
  505. return _default_from_email
  506. def default_database_options():
  507. """Database type dependent options"""
  508. if DATABASE.ENGINE.get().endswith('oracle'):
  509. return {'threaded': True}
  510. elif DATABASE.ENGINE.get().endswith('sqlite3'):
  511. return {'timeout': 30}
  512. else:
  513. return {}
  514. def get_deprecated_login_lock_out_by_combination_browser_user_agent():
  515. """Return value of deprecated LOGIN_LOCK_OUT_BY_COMBINATION_BROWSER_USER_AGENT_AND_IP config"""
  516. return AUTH.LOGIN_LOCK_OUT_BY_COMBINATION_BROWSER_USER_AGENT_AND_IP.get()
  517. SMTP = ConfigSection(
  518. key='smtp',
  519. help=_('Configuration options for connecting to an external SMTP server.'),
  520. members=dict(
  521. HOST = Config(
  522. key="host",
  523. help=_("The SMTP server for email notification delivery."),
  524. type=str,
  525. default="localhost"
  526. ),
  527. PORT = Config(
  528. key="port",
  529. help=_("The SMTP server port."),
  530. type=int,
  531. default=25
  532. ),
  533. USER = Config(
  534. key="user",
  535. help=_("The username for the SMTP host."),
  536. type=str,
  537. default=""
  538. ),
  539. PASSWORD = Config(
  540. key="password",
  541. help=_("The password for the SMTP user."),
  542. type=str,
  543. private=True,
  544. default="",
  545. ),
  546. PASSWORD_SCRIPT = Config(
  547. key="password_script",
  548. help=_("Execute this script to produce the SMTP user password. This will be used when the SMTP `password` is not set."),
  549. type=coerce_password_from_script,
  550. private=True,
  551. default="",
  552. ),
  553. USE_TLS = Config(
  554. key="tls",
  555. help=_("Whether to use a TLS (secure) connection when talking to the SMTP server."),
  556. type=coerce_bool,
  557. default=False
  558. ),
  559. DEFAULT_FROM= Config(
  560. key="default_from_email",
  561. help=_("Default email address to use for various automated notifications from Hue."),
  562. type=str,
  563. dynamic_default=default_from_email
  564. ),
  565. )
  566. )
  567. METRICS = ConfigSection(
  568. key='metrics',
  569. help=_("""Configuration options for metrics"""),
  570. members=dict(
  571. ENABLE_WEB_METRICS=Config(
  572. key='enable_web_metrics',
  573. help=_('Enable metrics URL "desktop/metrics"'),
  574. default=True,
  575. type=coerce_bool),
  576. LOCATION=Config(
  577. key='location',
  578. help=_('If specified, Hue will write metrics to this file'),
  579. type=str),
  580. COLLECTION_INTERVAL=Config(
  581. key='collection_interval',
  582. help=_('Time in milliseconds on how frequently to collect metrics'),
  583. type=coerce_positive_integer,
  584. default=30000),
  585. )
  586. )
  587. ANALYTICS = ConfigSection(
  588. key='analytics',
  589. help=_("""Configuration options for analytics user usage for admins"""),
  590. members=dict(
  591. IS_ENABLED=Config(
  592. key='is_enabled',
  593. help=_('Enable analytics page'),
  594. default=False,
  595. type=coerce_bool),
  596. )
  597. )
  598. DATABASE = ConfigSection(
  599. key='database',
  600. help=_("""Configuration options for specifying the Desktop Database.
  601. For more info, see http://docs.djangoproject.com/en/1.4/ref/settings/#database-engine"""),
  602. members=dict(
  603. ENGINE=Config(
  604. key='engine',
  605. help=_('Database engine, such as postgresql_psycopg2, mysql, or sqlite3.'),
  606. type=coerce_database,
  607. default='django.db.backends.sqlite3',
  608. ),
  609. NAME=Config(
  610. key='name',
  611. help=_('Database name, or path to DB if using sqlite3.'),
  612. type=str,
  613. default=get_desktop_root('desktop.db'),
  614. ),
  615. USER=Config(
  616. key='user',
  617. help=_('Database username.'),
  618. type=str,
  619. default='',
  620. ),
  621. SCHEMA=Config(
  622. key='schema',
  623. help=_('Database schema, to be used only when public schema is revoked in postgres.'),
  624. type=str,
  625. default='public',
  626. ),
  627. PASSWORD=Config(
  628. key='password',
  629. help=_('Database password.'),
  630. private=True,
  631. type=str,
  632. default="",
  633. ),
  634. PASSWORD_SCRIPT=Config(
  635. key='password_script',
  636. help=_('Execute this script to produce the database password. This will be used when `password` is not set.'),
  637. private=True,
  638. type=coerce_password_from_script,
  639. default="",
  640. ),
  641. HOST=Config(
  642. key='host',
  643. help=_('Database host.'),
  644. type=str,
  645. default='',
  646. ),
  647. PORT=Config(
  648. key='port',
  649. help=_('Database port.'),
  650. type=coerce_port,
  651. default='',
  652. ),
  653. OPTIONS=Config(
  654. key='options',
  655. help=_('Database options to send to the server when connecting.'),
  656. type=coerce_json_dict,
  657. dynamic_default=default_database_options
  658. ),
  659. CONN_MAX_AGE=Config(
  660. key='conn_max_age',
  661. help=_('The CONN_MAX_AGE parameter controls db connections persistency in seconds.'),
  662. type=coerce_zero_or_positive_integer,
  663. default=0,
  664. )
  665. )
  666. )
  667. SESSION = ConfigSection(
  668. key='session',
  669. help=_("""Configuration options for specifying the Desktop session.
  670. For more info, see https://docs.djangoproject.com/en/1.4/topics/http/sessions/"""),
  671. members=dict(
  672. COOKIE_NAME=Config(
  673. key='cookie_name',
  674. help=_("The name of the cookie to use for sessions."
  675. "This can have any value that is not used by the other cookie names in your application."),
  676. type=str,
  677. default="sessionid",
  678. ),
  679. TTL=Config(
  680. key='ttl',
  681. help=_("The cookie containing the users' session ID will expire after this amount of time in seconds."),
  682. type=int,
  683. default=60*60*24*14,
  684. ),
  685. SECURE=Config(
  686. key='secure',
  687. help=_("The cookie containing the users' session ID will be secure. This should only be enabled with HTTPS."),
  688. type=coerce_bool,
  689. dynamic_default=default_secure_cookie,
  690. ),
  691. HTTP_ONLY=Config(
  692. key='http_only',
  693. help=_("The cookie containing the users' session ID will use the HTTP only flag."),
  694. type=coerce_bool,
  695. default=True,
  696. ),
  697. EXPIRE_AT_BROWSER_CLOSE=Config(
  698. key='expire_at_browser_close',
  699. help=_("Use session-length cookies. Logs out the user when she closes the browser window."),
  700. type=coerce_bool,
  701. default=False
  702. ),
  703. CONCURRENT_USER_SESSION_LIMIT = Config(
  704. key="concurrent_user_session_limit",
  705. help=_("If set, limits the number of concurrent user sessions. 1 represents 1 session per user. Default: 0 (unlimited sessions per user)"),
  706. type=int,
  707. default=0,
  708. ),
  709. TRUSTED_ORIGINS = Config(
  710. key="trusted_origins",
  711. help=_("A list of hosts which are trusted origins for unsafe requests. See django's CSRF_TRUSTED_ORIGINS for more information"),
  712. type=coerce_csv,
  713. default='.cloudera.com',
  714. )
  715. )
  716. )
  717. KNOX = ConfigSection(
  718. key="knox",
  719. help=_("""Configuration options for specifying Hue's KNOX integration for secured Hadoop clusters."""),
  720. members=dict(
  721. KNOX_PRINCIPAL=Config(
  722. key='knox_principal',
  723. help=_("Comma separated list of Kerberos principal name for Hue. Typically 'knox/hostname.foo.com'."),
  724. type=coerce_csv,
  725. default="knox/%s" % socket.getfqdn()),
  726. KNOX_PROXYHOSTS = Config(
  727. key='knox_proxyhosts',
  728. default="%s" % socket.getfqdn(),
  729. type=coerce_csv,
  730. help=_('Comma separated list of strings representing the host names that the Hue server can trust as knox hosts.')
  731. ),
  732. KNOX_PORTS = Config(
  733. key='knox_ports',
  734. default=['80', '8443'],
  735. type=coerce_csv,
  736. help=_('Comma separated list of strings representing the ports that the Hue server can trust as knox port.')
  737. ),
  738. )
  739. )
  740. KERBEROS = ConfigSection(
  741. key="kerberos",
  742. help=_("""Configuration options for specifying Hue's Kerberos integration for
  743. secured Hadoop clusters."""),
  744. members=dict(
  745. HUE_KEYTAB=Config(
  746. key='hue_keytab',
  747. help=_("Path to a Kerberos keytab file containing Hue's service credentials."),
  748. type=str,
  749. default=None),
  750. HUE_PRINCIPAL=Config(
  751. key='hue_principal',
  752. help=_("Kerberos principal name for Hue. Typically 'hue/hostname.foo.com'."),
  753. type=str,
  754. default="hue/%s" % socket.getfqdn()),
  755. KEYTAB_REINIT_FREQUENCY=Config(
  756. key='reinit_frequency',
  757. help=_("Frequency in seconds with which Hue will renew its keytab."),
  758. type=int,
  759. default=60*60), #1h
  760. CCACHE_PATH=Config(
  761. key='ccache_path',
  762. help=_("Path to keep Kerberos credentials cached."),
  763. private=True,
  764. type=str,
  765. default="/var/run/hue/hue_krb5_ccache",
  766. ),
  767. KINIT_PATH=Config(
  768. key='kinit_path',
  769. help=_("Path to Kerberos 'kinit' command."),
  770. type=str,
  771. default="kinit", # use PATH!
  772. ),
  773. MUTUAL_AUTHENTICATION=Config(
  774. key='mutual_authentication',
  775. help=_('Mutual authentication from the server, attaches HTTP GSSAPI/Kerberos Authentication to the given Request object'),
  776. type=str,
  777. default="OPTIONAL",
  778. ),
  779. )
  780. )
  781. SASL_MAX_BUFFER = Config(
  782. key="sasl_max_buffer",
  783. help=_("This property specifies the maximum size of the receive buffer in bytes in thrift sasl communication."),
  784. default=2*1024*1024, # 2 MB
  785. type=int
  786. )
  787. ENABLE_SMART_THRIFT_POOL = Config(
  788. key="enable_smart_thrift_pool",
  789. help=_("Hue will try to get the actual host of the Service, even if it resides behind a load balancer. "
  790. "This will enable an automatic configuration of the service without requiring custom configuration of the service load balancer. "
  791. "This is available for the Impala service only currently. It is highly recommended to only point to a series of coordinator-only nodes only."),
  792. type=coerce_bool,
  793. default=False
  794. )
  795. # See python's documentation for time.tzset for valid values.
  796. TIME_ZONE = Config(
  797. key="time_zone",
  798. help=_("Time zone name."),
  799. type=str,
  800. default=os.environ.get("TZ", "America/Los_Angeles")
  801. )
  802. DEFAULT_SITE_ENCODING = Config(
  803. key='default_site_encoding',
  804. help=_('Default system-wide unicode encoding.'),
  805. type=str,
  806. default='utf-8'
  807. )
  808. SERVER_USER = Config(
  809. key="server_user",
  810. help=_("Username to run servers as."),
  811. type=str,
  812. default="hue")
  813. SERVER_GROUP = Config(
  814. key="server_group",
  815. help=_("Group to run servers as."),
  816. type=str,
  817. default="hue")
  818. DEFAULT_USER = Config(
  819. key="default_user",
  820. help=_("This should be the user running hue webserver"),
  821. type=str,
  822. default="hue")
  823. DEFAULT_HDFS_SUPERUSER = Config(
  824. key="default_hdfs_superuser",
  825. help=_("This should be the hdfs super user"),
  826. type=str,
  827. default="hdfs")
  828. CUSTOM = ConfigSection(
  829. key="custom",
  830. help=_("Customizations to the UI."),
  831. members=dict(
  832. BANNER_TOP_HTML=Config("banner_top_html",
  833. default="",
  834. help=_("Top banner HTML code. This code will be placed in the navigation bar "
  835. "so that it will reside at the top of the page in a fixed position. " +
  836. "One common value is `<img src=\"http://www.example.com/example.gif\" />`")),
  837. LOGIN_SPLASH_HTML=Config("login_splash_html",
  838. default="",
  839. help=_("The login splash HTML code. This code will be placed in the login page, "
  840. "useful for security warning messages.")),
  841. CACHEABLE_TTL=Config("cacheable_ttl",
  842. default=10 * 24 * 60 * 60 * 1000,
  843. type=int,
  844. help=_("The cache TTL in milliseconds for the assist/autocomplete/etc calls. Set to 0 it disables the cache.")),
  845. LOGO_SVG=Config("logo_svg",
  846. default="",
  847. help=_("SVG code to replace the default Hue logo in the top bar and sign in screen")),
  848. ))
  849. AUTH = ConfigSection(
  850. key="auth",
  851. help=_("Configuration options for user authentication into the web application."),
  852. members=dict(
  853. BACKEND=Config("backend",
  854. default=["desktop.auth.backend.AllowFirstUserDjangoBackend"],
  855. type=coerce_csv,
  856. help=_("Authentication backend. Common settings are "
  857. "django.contrib.auth.backends.ModelBackend (fully Django backend), " +
  858. "desktop.auth.backend.AllowAllBackend (allows everyone), " +
  859. "desktop.auth.backend.AllowFirstUserDjangoBackend (relies on Django and user manager, after the first login). " +
  860. "Multiple Authentication backends are supported by specifying a comma-separated list in order of priority.")),
  861. USER_AUGMENTOR=Config("user_augmentor",
  862. default="desktop.auth.backend.DefaultUserAugmentor",
  863. help=_("Class which defines extra accessor methods for User objects.")),
  864. PAM_SERVICE=Config("pam_service",
  865. default="login",
  866. help=_("The service to use when querying PAM. "
  867. "The service usually corresponds to a single filename in /etc/pam.d")),
  868. REMOTE_USER_HEADER=Config("remote_user_header",
  869. default="HTTP_REMOTE_USER",
  870. help=_("When using the desktop.auth.backend.RemoteUserDjangoBackend, this sets "
  871. "the normalized name of the header that contains the remote user. "
  872. "The HTTP header in the request is converted to a key by converting "
  873. "all characters to uppercase, replacing any hyphens with underscores "
  874. "and adding an HTTP_ prefix to the name. So, for example, if the header "
  875. "is called Remote-User that would be configured as HTTP_REMOTE_USER")),
  876. IGNORE_USERNAME_CASE = Config("ignore_username_case",
  877. help=_("Ignore the case of usernames when searching for existing users in Hue."),
  878. type=coerce_bool,
  879. default=True),
  880. FORCE_USERNAME_LOWERCASE = Config("force_username_lowercase",
  881. help=_("Force usernames to lowercase when creating new users."),
  882. type=coerce_bool,
  883. default=True),
  884. FORCE_USERNAME_UPPERCASE = Config("force_username_uppercase",
  885. help=_("Force usernames to uppercase when creating new users."),
  886. type=coerce_bool,
  887. default=False),
  888. EXPIRES_AFTER = Config("expires_after",
  889. help=_("Users will expire after they have not logged in for 'n' amount of seconds."
  890. "A negative number means that users will never expire."),
  891. type=int,
  892. default=-1),
  893. EXPIRE_SUPERUSERS = Config("expire_superusers",
  894. help=_("Apply 'expires_after' to superusers."),
  895. type=coerce_bool,
  896. default=True),
  897. IDLE_SESSION_TIMEOUT = Config("idle_session_timeout",
  898. help=_("Users will automatically be logged out after 'n' seconds of inactivity."
  899. "A negative number means that idle sessions will not be timed out."),
  900. type=int,
  901. default=-1),
  902. CHANGE_DEFAULT_PASSWORD = Config(
  903. key="change_default_password",
  904. help=_("When set to true this will allow you to specify a password for "
  905. "the user when you create the user and then force them to change "
  906. "their password upon first login. The default is false."),
  907. type=coerce_bool,
  908. default=False,
  909. ),
  910. LOGIN_FAILURE_LIMIT = Config(
  911. key="login_failure_limit",
  912. help=_("Number of login attempts allowed before a record is created for failed logins"),
  913. type=int,
  914. default=3,
  915. ),
  916. LOGIN_LOCK_OUT_AT_FAILURE = Config(
  917. key="login_lock_out_at_failure",
  918. help=_("After number of allowed login attempts are exceeded, do we lock out this IP and optionally user agent?"),
  919. type=coerce_bool,
  920. default=False,
  921. ),
  922. LOGIN_COOLOFF_TIME = Config(
  923. key="login_cooloff_time",
  924. help=_("If set, defines period of inactivity in hours after which failed logins will be forgotten."
  925. "A value of 0 or None will disable this check. Default: None."),
  926. type=coerce_timedelta,
  927. default=None,
  928. ),
  929. # Deprecated by LOGIN_LOCK_OUT_USE_USER_AGENT
  930. LOGIN_LOCK_OUT_BY_COMBINATION_BROWSER_USER_AGENT_AND_IP=Config(
  931. key="login_lock_out_by_combination_browser_user_agent_and_ip",
  932. help=_("If True, lock out based on IP and browser user agent"),
  933. type=coerce_bool,
  934. default=False,
  935. ),
  936. LOGIN_LOCK_OUT_USE_USER_AGENT = Config(
  937. key="login_lock_out_use_user_agent",
  938. help=_("If True, lock out based on an IP address AND a user agent."
  939. "This means requests from different user agents but from the same IP are treated differently."),
  940. type=coerce_bool,
  941. dynamic_default=get_deprecated_login_lock_out_by_combination_browser_user_agent
  942. ),
  943. LOGIN_LOCK_OUT_BY_COMBINATION_USER_AND_IP = Config(
  944. key="login_lock_out_by_combination_user_and_ip",
  945. help=_("If True, lock out based on IP and user"),
  946. type=coerce_bool,
  947. default=False,
  948. ),
  949. BEHIND_REVERSE_PROXY = Config(
  950. key="behind_reverse_proxy",
  951. help=_("If True, it will look for the IP address from the header defined at reverse_proxy_header."),
  952. type=coerce_bool,
  953. dynamic_default=is_lb_enabled,
  954. ),
  955. REVERSE_PROXY_HEADER = Config(
  956. key="reverse_proxy_header",
  957. help=_("If behind_reverse_proxy is True, it will look for the IP address from this header. Default: HTTP_X_FORWARDED_FOR"),
  958. type=str,
  959. default="HTTP_X_FORWARDED_FOR",
  960. )
  961. ))
  962. LDAP = ConfigSection(
  963. key="ldap",
  964. help=_("Configuration options for LDAP connectivity."),
  965. members=dict(
  966. CREATE_USERS_ON_LOGIN = Config("create_users_on_login",
  967. help=_("Create users when they login with their LDAP credentials."),
  968. type=coerce_bool,
  969. default=True),
  970. SYNC_GROUPS_ON_LOGIN = Config("sync_groups_on_login",
  971. help=_("Synchronize a users groups when they login."),
  972. type=coerce_bool,
  973. default=True),
  974. IGNORE_USERNAME_CASE = Config("ignore_username_case",
  975. help=_("Ignore the case of usernames when searching for existing users in Hue."),
  976. type=coerce_bool,
  977. default=True),
  978. FORCE_USERNAME_LOWERCASE = Config("force_username_lowercase",
  979. help=_("Force usernames to lowercase when creating new users from LDAP."),
  980. type=coerce_bool,
  981. default=True),
  982. FORCE_USERNAME_UPPERCASE = Config("force_username_uppercase",
  983. help=_("Force usernames to uppercase when creating new users from LDAP."),
  984. type=coerce_bool,
  985. default=False),
  986. SUBGROUPS = Config("subgroups",
  987. help=_("Choose which kind of subgrouping to use: nested or suboordinate (deprecated)."),
  988. type=coerce_str_lowercase,
  989. default="suboordinate"),
  990. NESTED_MEMBERS_SEARCH_DEPTH = Config("nested_members_search_depth",
  991. help=_("Define the number of levels to search for nested members."),
  992. type=int,
  993. default=10),
  994. FOLLOW_REFERRALS = Config("follow_referrals",
  995. help=_("Whether or not to follow referrals."),
  996. type=coerce_bool,
  997. default=False),
  998. LOGIN_GROUPS = Config("login_groups",
  999. help=_("A comma-separated list of Ldap groups with users that can login"),
  1000. type=coerce_csv,
  1001. default=[]),
  1002. DEBUG = Config("debug",
  1003. type=coerce_bool,
  1004. default=False,
  1005. help=_("Set to a value to enable python-ldap debugging.")),
  1006. DEBUG_LEVEL = Config("debug_level",
  1007. default=255,
  1008. type=int,
  1009. help=_("Sets the debug level within the underlying LDAP C lib.")),
  1010. TRACE_LEVEL = Config("trace_level",
  1011. default=0,
  1012. type=int,
  1013. help=_("Possible values for trace_level are 0 for no logging, 1 for only logging the method calls with arguments,"
  1014. "2 for logging the method calls with arguments and the complete results and 9 for also logging the traceback of method calls.")),
  1015. LDAP_SERVERS = UnspecifiedConfigSection(
  1016. key="ldap_servers",
  1017. help=_("LDAP server record."),
  1018. each=ConfigSection(
  1019. members=dict(
  1020. BASE_DN=Config("base_dn",
  1021. default=None,
  1022. help=_("The base LDAP distinguished name to use for LDAP search.")),
  1023. NT_DOMAIN=Config("nt_domain",
  1024. default=None,
  1025. help=_("The NT domain used for LDAP authentication.")),
  1026. LDAP_URL=Config("ldap_url",
  1027. default=None,
  1028. help=_("The LDAP URL to connect to.")),
  1029. USE_START_TLS=Config("use_start_tls",
  1030. default=True,
  1031. type=coerce_bool,
  1032. help=_("Use StartTLS when communicating with LDAP server.")),
  1033. LDAP_CERT=Config("ldap_cert",
  1034. default=None,
  1035. 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.")),
  1036. LDAP_USERNAME_PATTERN=Config("ldap_username_pattern",
  1037. default=None,
  1038. help=_("A pattern to use for constructing LDAP usernames.")),
  1039. BIND_DN=Config("bind_dn",
  1040. default=None,
  1041. help=_("The distinguished name to bind as, when importing from LDAP.")),
  1042. BIND_PASSWORD=Config("bind_password",
  1043. default=None,
  1044. private=True,
  1045. help=_("The password for the bind user.")),
  1046. BIND_PASSWORD_SCRIPT=Config("bind_password_script",
  1047. default=None,
  1048. private=True,
  1049. type=coerce_password_from_script,
  1050. help=_("Execute this script to produce the LDAP bind user password. This will be used when `bind_password` is not set.")),
  1051. SEARCH_BIND_AUTHENTICATION=Config("search_bind_authentication",
  1052. default=True,
  1053. type=coerce_bool,
  1054. help=_("Use search bind authentication.")),
  1055. FOLLOW_REFERRALS = Config("follow_referrals",
  1056. help=_("Whether or not to follow referrals."),
  1057. type=coerce_bool,
  1058. default=False),
  1059. TEST_LDAP_USER=Config("test_ldap_user",
  1060. default=None,
  1061. help=_("The test user name to use for LDAP search.")),
  1062. TEST_LDAP_GROUP=Config("test_ldap_group",
  1063. default=None,
  1064. help=_("The test group name to use for LDAP search.")),
  1065. DEBUG = Config("debug",
  1066. type=coerce_bool,
  1067. default=False,
  1068. help=_("Set to a value to enable python-ldap debugging.")),
  1069. DEBUG_LEVEL = Config("debug_level",
  1070. default=255,
  1071. type=int,
  1072. help=_("Sets the debug level within the underlying LDAP C lib.")),
  1073. TRACE_LEVEL = Config("trace_level",
  1074. default=0,
  1075. type=int,
  1076. help=_("Possible values for trace_level are 0 for no logging, 1 for only logging the method calls with arguments,"
  1077. "2 for logging the method calls with arguments and the complete results and 9 for also logging the traceback of method calls.")),
  1078. USERS = ConfigSection(
  1079. key="users",
  1080. help=_("Configuration for LDAP user schema and search."),
  1081. members=dict(
  1082. USER_FILTER=Config("user_filter",
  1083. default="objectclass=*",
  1084. help=_("A base filter for use when searching for users.")),
  1085. USER_NAME_ATTR=Config("user_name_attr",
  1086. default="sAMAccountName",
  1087. help=_("The username attribute in the LDAP schema. "
  1088. "Typically, this is 'sAMAccountName' for AD and 'uid' "
  1089. "for other LDAP systems.")),
  1090. )
  1091. ),
  1092. GROUPS = ConfigSection(
  1093. key="groups",
  1094. help=_("Configuration for LDAP group schema and search."),
  1095. members=dict(
  1096. GROUP_FILTER=Config("group_filter",
  1097. default="objectclass=*",
  1098. help=_("A base filter for use when searching for groups.")),
  1099. GROUP_NAME_ATTR=Config("group_name_attr",
  1100. default="cn",
  1101. help=_("The group name attribute in the LDAP schema. "
  1102. "Typically, this is 'cn'.")),
  1103. GROUP_MEMBER_ATTR=Config("group_member_attr",
  1104. default="member",
  1105. help=_("The LDAP attribute which specifies the "
  1106. "members of a group.")),
  1107. ))))),
  1108. # Every thing below here is deprecated and should be removed in an upcoming major release.
  1109. BASE_DN=Config("base_dn",
  1110. default=None,
  1111. help=_("The base LDAP distinguished name to use for LDAP search.")),
  1112. NT_DOMAIN=Config("nt_domain",
  1113. default=None,
  1114. help=_("The NT domain used for LDAP authentication.")),
  1115. LDAP_URL=Config("ldap_url",
  1116. default=None,
  1117. help=_("The LDAP URL to connect to.")),
  1118. USE_START_TLS=Config("use_start_tls",
  1119. default=True,
  1120. type=coerce_bool,
  1121. help=_("Use StartTLS when communicating with LDAP server.")),
  1122. LDAP_CERT=Config("ldap_cert",
  1123. default=None,
  1124. 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.")),
  1125. LDAP_USERNAME_PATTERN=Config("ldap_username_pattern",
  1126. default=None,
  1127. help=_("A pattern to use for constructing LDAP usernames.")),
  1128. BIND_DN=Config("bind_dn",
  1129. default=None,
  1130. help=_("The distinguished name to bind as, when importing from LDAP.")),
  1131. BIND_PASSWORD=Config("bind_password",
  1132. default=None,
  1133. private=True,
  1134. help=_("The password for the bind user.")),
  1135. BIND_PASSWORD_SCRIPT=Config("bind_password_script",
  1136. default=None,
  1137. private=True,
  1138. type=coerce_password_from_script,
  1139. help=_("Execute this script to produce the LDAP bind user password. This will be used when `bind_password` is not set.")),
  1140. SEARCH_BIND_AUTHENTICATION=Config("search_bind_authentication",
  1141. default=True,
  1142. type=coerce_bool,
  1143. help=_("Use search bind authentication.")),
  1144. TEST_LDAP_USER=Config("test_ldap_user",
  1145. default=None,
  1146. help=_("The test user name to use for LDAP search.")),
  1147. TEST_LDAP_GROUP=Config("test_ldap_group",
  1148. default=None,
  1149. help=_("The test group name to use for LDAP search.")),
  1150. USERS = ConfigSection(
  1151. key="users",
  1152. help=_("Configuration for LDAP user schema and search."),
  1153. members=dict(
  1154. USER_FILTER=Config("user_filter",
  1155. default="objectclass=*",
  1156. help=_("A base filter for use when searching for users.")),
  1157. USER_NAME_ATTR=Config("user_name_attr",
  1158. default="sAMAccountName",
  1159. help=_("The username attribute in the LDAP schema. "
  1160. "Typically, this is 'sAMAccountName' for AD and 'uid' "
  1161. "for other LDAP systems.")),
  1162. )),
  1163. GROUPS = ConfigSection(
  1164. key="groups",
  1165. help=_("Configuration for LDAP group schema and search."),
  1166. members=dict(
  1167. GROUP_FILTER=Config("group_filter",
  1168. default="objectclass=*",
  1169. help=_("A base filter for use when searching for groups.")),
  1170. GROUP_NAME_ATTR=Config("group_name_attr",
  1171. default="cn",
  1172. help=_("The group name attribute in the LDAP schema. "
  1173. "Typically, this is 'cn'.")),
  1174. GROUP_MEMBER_ATTR=Config("group_member_attr",
  1175. default="member",
  1176. help=_("The LDAP attribute which specifies the "
  1177. "members of a group.")),
  1178. ))))
  1179. OAUTH = ConfigSection(
  1180. key='oauth',
  1181. help=_('Configuration options for Oauth 1.0 authentication'),
  1182. members=dict(
  1183. CONSUMER_KEY = Config(
  1184. key="consumer_key",
  1185. help=_("The Consumer key of the application."),
  1186. type=str,
  1187. default="XXXXXXXXXXXXXXXXXXXXX"
  1188. ),
  1189. CONSUMER_SECRET = Config(
  1190. key="consumer_secret",
  1191. help=_("The Consumer secret of the application."),
  1192. type=str,
  1193. default="XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
  1194. ),
  1195. REQUEST_TOKEN_URL = Config(
  1196. key="request_token_url",
  1197. help=_("The Request token URL."),
  1198. type=str,
  1199. default="https://api.twitter.com/oauth/request_token"
  1200. ),
  1201. ACCESS_TOKEN_URL = Config(
  1202. key="access_token_url",
  1203. help=_("The Access token URL."),
  1204. type=str,
  1205. default="https://api.twitter.com/oauth/access_token"
  1206. ),
  1207. AUTHENTICATE_URL = Config(
  1208. key="authenticate_url",
  1209. help=_("The Authorize URL."),
  1210. type=str,
  1211. default="https://api.twitter.com/oauth/authorize"
  1212. ),
  1213. )
  1214. )
  1215. OIDC = ConfigSection(
  1216. key='oidc',
  1217. help=_('Configuration options for OpenID Connect authentication'),
  1218. members=dict(
  1219. OIDC_RP_CLIENT_ID = Config(
  1220. key="oidc_rp_client_id",
  1221. help=_("The client ID as relay party set in OpenID provider."),
  1222. type=str,
  1223. default="XXXXXXXXXXXXXXXXXXXXX"
  1224. ),
  1225. OIDC_RP_CLIENT_SECRET = Config(
  1226. key="oidc_rp_client_secret",
  1227. help=_("The client secret as relay party set in OpenID provider."),
  1228. type=str,
  1229. default="XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
  1230. ),
  1231. OIDC_OP_AUTHORIZATION_ENDPOINT = Config(
  1232. key="oidc_op_authorization_endpoint",
  1233. help=_("The OpenID provider authoriation endpoint."),
  1234. type=str,
  1235. default="https://keycloak.example.com/auth/realms/Cloudera/protocol/openid-connect/auth"
  1236. ),
  1237. OIDC_OP_TOKEN_ENDPOINT = Config(
  1238. key="oidc_op_token_endpoint",
  1239. help=_("The OpenID provider token endpoint."),
  1240. type=str,
  1241. default="https://keycloak.example.com/auth/realms/cloudera/protocol/openid-connect/token"
  1242. ),
  1243. AUTHENTICATE_URL = Config(
  1244. key="authenticate_url",
  1245. help=_("The Authorize URL."),
  1246. type=str,
  1247. default="https://api.twitter.com/oauth/authorize"
  1248. ),
  1249. OIDC_OP_USER_ENDPOINT=Config(
  1250. key="oidc_op_user_endpoint",
  1251. help=_("The OpenID provider user info endpoint."),
  1252. type=str,
  1253. default="https://keycloak.example.com/auth/realms/cloudera/protocol/openid-connect/userinfo"
  1254. ),
  1255. OIDC_RP_IDP_SIGN_KEY=Config(
  1256. key="oidc_rp_idp_sign_key",
  1257. help=_("The OpenID provider signing key in PEM or DER format."),
  1258. type=str,
  1259. default=None
  1260. ),
  1261. OIDC_OP_JWKS_ENDPOINT=Config(
  1262. key="oidc_op_jwks_endpoint",
  1263. help=_("The OpenID provider authoriation endpoint."),
  1264. type=str,
  1265. default="https://keycloak.example.com/auth/realms/Cloudera/protocol/openid-connect/certs"
  1266. ),
  1267. OIDC_VERIFY_SSL=Config(
  1268. key="oidc_verify_ssl",
  1269. help=_("Whether Hue as OpenID Connect client verify SSL cert."),
  1270. type=coerce_bool,
  1271. default=False
  1272. ),
  1273. LOGIN_REDIRECT_URL=Config(
  1274. key="login_redirect_url",
  1275. help=_("As relay party Hue URL path to redirect to after login."),
  1276. type=str,
  1277. default="https://localhost:8888/oidc/callback/"
  1278. ),
  1279. LOGOUT_REDIRECT_URL=Config(
  1280. key="logout_redirect_url",
  1281. help=_("The OpenID provider URL path to redirect to after logout."),
  1282. type=str,
  1283. default="https://keycloak.example.com/auth/realms/cloudera/protocol/openid-connect/logout"
  1284. ),
  1285. LOGIN_REDIRECT_URL_FAILURE=Config(
  1286. key="login_redirect_url_failure",
  1287. help=_("As relay party Hue URL path to redirect to after login."),
  1288. type=str,
  1289. default="https://localhost:8888/hue/oidc_failed/"
  1290. ),
  1291. CREATE_USERS_ON_LOGIN=Config(
  1292. key="create_users_on_login",
  1293. help=_("Create a new user from OpenID Connect on login if it doesn't exist."),
  1294. type=coerce_bool,
  1295. default=True
  1296. ),
  1297. OIDC_USERNAME_ATTRIBUTE=Config(
  1298. key="oidc_username_attribute",
  1299. help=_("The attribute to be used as username when creating and looking up the user."),
  1300. type=str,
  1301. default="preferred_username"
  1302. ),
  1303. SUPERUSER_GROUP=Config(
  1304. key="superuser_group",
  1305. help=_("The group of users will be created and updated as superuser."),
  1306. type=str,
  1307. default=""
  1308. ),
  1309. )
  1310. )
  1311. LOCAL_FILESYSTEMS = UnspecifiedConfigSection(
  1312. key="local_filesystems",
  1313. help=_("Paths on the local file system that users should be able to browse."),
  1314. each=ConfigSection(
  1315. members=dict(
  1316. PATH=Config("path",
  1317. required=True,
  1318. help=_("The path on the local filesystem.")))))
  1319. def default_feedback_url():
  1320. """A version-specific URL."""
  1321. return "http://groups.google.com/a/cloudera.org/group/hue-user"
  1322. FEEDBACK_URL = Config(
  1323. key="feedback_url",
  1324. help=_("Link for 'feedback' tab."),
  1325. type=str,
  1326. dynamic_default=default_feedback_url
  1327. )
  1328. SEND_DBUG_MESSAGES = Config(
  1329. key="send_dbug_messages",
  1330. help=_("Whether to send debug messages from JavaScript to the server logs."),
  1331. type=coerce_bool,
  1332. default=False
  1333. )
  1334. DATABASE_LOGGING = Config(
  1335. key="database_logging",
  1336. help=_("Enable or disable database debug mode."),
  1337. type=coerce_bool,
  1338. default=False
  1339. )
  1340. DJANGO_ADMINS = UnspecifiedConfigSection(
  1341. key="django_admins",
  1342. help=_("Administrators that should receive error emails."),
  1343. each=ConfigSection(
  1344. members=dict(
  1345. NAME=Config("name",
  1346. required=True,
  1347. help=_("The full name of the admin.")),
  1348. EMAIL=Config("email",
  1349. required=True,
  1350. help=_("The email address of the admin.")))))
  1351. DJANGO_DEBUG_MODE = Config(
  1352. key="django_debug_mode",
  1353. help=_("Enable or disable debug mode."),
  1354. type=coerce_bool,
  1355. default=True
  1356. )
  1357. DEV = Config("dev",
  1358. type=coerce_bool,
  1359. default=False,
  1360. help=_("Enable development mode, where notably static files are not cached.")
  1361. )
  1362. DISPLAY_APP_SWITCHER = Config(
  1363. key='display_app_switcher',
  1364. help=_('Enable or disable the upper left app switcher menu.'),
  1365. type=coerce_bool,
  1366. default=False
  1367. )
  1368. APP_SWITCHER_ALTUS_BASE_URL = Config(
  1369. key="app_switcher_altus_base_url",
  1370. help=_("Optional altus base url to use for the app switcher."),
  1371. type=str,
  1372. default=''
  1373. )
  1374. APP_SWITCHER_MOW_BASE_URL = Config(
  1375. key="app_switcher_mow_base_url",
  1376. help=_("Optional mow base url to use for the app switcher."),
  1377. type=str,
  1378. default=''
  1379. )
  1380. HTTP_500_DEBUG_MODE = Config(
  1381. key='http_500_debug_mode',
  1382. help=_('Enable or disable debugging information in the 500 internal server error response. '
  1383. 'Note that the debugging information may contain sensitive data. '
  1384. 'If django_debug_mode is True, this is automatically enabled.'),
  1385. type=coerce_bool,
  1386. default=True
  1387. )
  1388. def get_instrumentation_default():
  1389. """If django_debug_mode is True, this is automatically enabled"""
  1390. return DJANGO_DEBUG_MODE.get()
  1391. INSTRUMENTATION = Config(
  1392. key='instrumentation',
  1393. help=_('Enable or disable instrumentation. If django_debug_mode is True, this is automatically enabled.'),
  1394. type=coerce_bool,
  1395. dynamic_default=get_instrumentation_default)
  1396. AUDIT_EVENT_LOG_DIR = Config(
  1397. key="audit_event_log_dir",
  1398. help=_("The directory where to store the auditing logs. Auditing is disable if the value is empty."),
  1399. type=str,
  1400. dynamic_default=get_navigator_audit_log_dir
  1401. )
  1402. AUDIT_LOG_MAX_FILE_SIZE = Config(
  1403. key="audit_log_max_file_size",
  1404. help=_("Size in KB/MB/GB for audit log to rollover."),
  1405. type=str,
  1406. dynamic_default=get_navigator_audit_max_file_size
  1407. )
  1408. DJANGO_SERVER_EMAIL = Config(
  1409. key='django_server_email',
  1410. help=_('Email address that internal error messages should send as.'),
  1411. default='hue@localhost.localdomain'
  1412. )
  1413. DJANGO_EMAIL_BACKEND = Config(
  1414. key="django_email_backend",
  1415. help=_("The email backend to use."),
  1416. type=str,
  1417. default="django.core.mail.backends.smtp.EmailBackend"
  1418. )
  1419. ENABLE_SQL_SYNTAX_CHECK = Config(
  1420. key='enable_sql_syntax_check',
  1421. default=True,
  1422. type=coerce_bool,
  1423. help=_('Choose whether to enable SQL syntax check or not.')
  1424. )
  1425. EDITOR_AUTOCOMPLETE_TIMEOUT = Config(
  1426. key='editor_autocomplete_timeout',
  1427. type=int,
  1428. default=30000,
  1429. help=_('Timeout value in ms for autocomplete of columns, tables, values etc. 0 = disabled.')
  1430. )
  1431. USE_NEW_EDITOR = Config( # To remove in Hue 4
  1432. key='',
  1433. default=True,
  1434. type=coerce_bool,
  1435. help=_('Choose whether to show the new SQL editor.')
  1436. )
  1437. ENABLE_DOWNLOAD = Config(
  1438. key="enable_download",
  1439. help=_(
  1440. 'Global setting to allow or disable end user downloads in all Hue (e.g. Query result in editors and dashboard, file in File Browser browsers...).'),
  1441. type=coerce_bool,
  1442. default=True)
  1443. ENABLE_DJANGO_DEBUG_TOOL = Config(
  1444. key="enable_django_debug_tool",
  1445. help=_('Allow use django debug tool with Chrome browser for debugging issue, django_debug_mode must be true also'),
  1446. type=coerce_bool,
  1447. default=False)
  1448. DJANGO_DEBUG_TOOL_USERS = Config(
  1449. key='django_debug_tool_users',
  1450. default='',
  1451. type=coerce_csv,
  1452. help=_('Comma separated list of users that allow to use django debug tool. If it is empty, all users are allowed.')
  1453. )
  1454. USE_NEW_SIDE_PANELS = Config( # To remove in Hue 4
  1455. key='use_new_side_panels',
  1456. default=True,
  1457. type=coerce_bool,
  1458. help=_('Choose whether to show extended left and right panels.')
  1459. )
  1460. USE_DEFAULT_CONFIGURATION = Config(
  1461. key='use_default_configuration',
  1462. default=False,
  1463. type=coerce_bool,
  1464. help=_('Enable saved default configurations for Hive, Impala, Spark, and Oozie.')
  1465. )
  1466. USE_NEW_CHARTS = Config(
  1467. key='use_new_charts',
  1468. default=False,
  1469. type=coerce_bool,
  1470. help=_('Choose whether to use new charting library across the whole Hue.')
  1471. )
  1472. # Deprecated
  1473. IS_MULTICLUSTER_ONLY = Config(
  1474. key='is_multicluster_only',
  1475. default=False,
  1476. type=coerce_bool,
  1477. help=_('Choose whether to pick configs only from [desktop] [[cluster]]')
  1478. )
  1479. # Deprecated
  1480. IS_K8S_ONLY = Config(
  1481. key='is_k8s_only',
  1482. default=False,
  1483. type=coerce_bool,
  1484. help=_('Choose whether to pick configs only from [desktop] [[cluster]]')
  1485. )
  1486. ENABLE_ORGANIZATIONS = Config(
  1487. key='enable_organizations',
  1488. default=False,
  1489. type=coerce_bool,
  1490. help=_('Choose whether to allow multi tenancy or not.')
  1491. )
  1492. ENABLE_PROMETHEUS = Config(
  1493. key='enable_prometheus',
  1494. default=False,
  1495. type=coerce_bool,
  1496. help=_('Turn on Prometheus metrics end point /metrics.')
  1497. )
  1498. TRACING = ConfigSection(
  1499. key="tracing",
  1500. help=_("Tracing configuration."),
  1501. members=dict(
  1502. ENABLED= Config(
  1503. key='enabled',
  1504. default=False,
  1505. type=coerce_bool,
  1506. help=_('If tracing is enabled.')
  1507. ),
  1508. TRACE_ALL = Config(
  1509. key='trace_all',
  1510. default=False,
  1511. type=coerce_bool,
  1512. help=_('Trace all the requests instead of a few specific ones like the SQL Editor. Much noisiers.')
  1513. ),
  1514. ))
  1515. def task_server_default_result_directory():
  1516. """Local directory to store task results."""
  1517. return 'file://%s' % get_run_root('logs')
  1518. TASK_SERVER = ConfigSection(
  1519. key="task_server",
  1520. help=_("Task Server configuration."),
  1521. members=dict(
  1522. ENABLED= Config(
  1523. key='enabled',
  1524. default=False,
  1525. type=coerce_bool,
  1526. help=_('If resource intensive or blocking can be delegated to an already running task server.')
  1527. ),
  1528. BROKER_URL = Config(
  1529. key='broker_url',
  1530. default='amqp://guest:guest@localhost//',
  1531. help=_('How the task server and tasks communicate.')
  1532. ),
  1533. CELERY_RESULT_BACKEND = Config(
  1534. key='celery_result_backend',
  1535. dynamic_default=task_server_default_result_directory,
  1536. help=_('Where to store task results. Defaults to local file system path. Celery comes with a several other backends.')
  1537. ),
  1538. RESULT_CELERYD_OPTS = Config(
  1539. key='celeryd_opts',
  1540. default='--time-limit=300',
  1541. help=_('Default options provided to the task server at startup.')
  1542. ),
  1543. BEAT_ENABLED = Config(
  1544. key='beat_enabled',
  1545. default=False,
  1546. type=coerce_bool,
  1547. help=_('Switch on the integration with the Task Scheduler.')
  1548. ),
  1549. BEAT_SCHEDULES_FILE = Config(
  1550. key='beat_schedules_file',
  1551. default='',
  1552. type=str,
  1553. help=_('Path to a file containing a list of beat schedules.')
  1554. ),
  1555. FETCH_RESULT_LIMIT = Config(
  1556. key='fetch_result_limit',
  1557. default=2000,
  1558. type=coerce_positive_integer,
  1559. help=_('Number of query results rows to fetch into the result storage.')
  1560. ),
  1561. RESULT_CACHE = Config(
  1562. key='result_cache',
  1563. type=str,
  1564. help=_('Django file cache class to use to temporarily store query results'),
  1565. default='{"BACKEND": "django_redis.cache.RedisCache", "LOCATION": "redis://localhost:6379/0", "OPTIONS": {"CLIENT_CLASS": "django_redis.client.DefaultClient"},"KEY_PREFIX": "queries"}'
  1566. ),
  1567. RESULT_STORAGE = Config(
  1568. key='result_storage',
  1569. type=str,
  1570. help=_('Django file storage class to use to persist query results'),
  1571. default='{"backend": "django.core.files.storage.FileSystemStorage", "properties": {"location": "./logs"}}'
  1572. ),
  1573. EXECUTION_STORAGE = Config(
  1574. key='execution_storage',
  1575. type=str,
  1576. help=_('Django cache to use to store temporarily used data during query execution. This is in addition to result_file_storage and result_backend.'),
  1577. default='{"BACKEND": "django.core.cache.backends.locmem.LocMemCache", "LOCATION": "celery-hue"}'
  1578. ),
  1579. ))
  1580. def get_clusters(user):
  1581. clusters = []
  1582. cluster_config = CLUSTERS.get()
  1583. # Backward compatibility when not using clusters
  1584. if not cluster_config:
  1585. clusters.append(
  1586. (CLUSTER_ID.get(), {
  1587. 'id': CLUSTER_ID.get(),
  1588. 'name': CLUSTER_ID.get(),
  1589. 'type': 'direct',
  1590. 'credentials': {},
  1591. }
  1592. )
  1593. )
  1594. for i in cluster_config:
  1595. # Get additional remote multi clusters
  1596. clusters.append(
  1597. (i, {
  1598. 'id': i,
  1599. 'name': cluster_config[i].NAME.get() or i,
  1600. 'type': cluster_config[i].TYPE.get(),
  1601. 'credentials': cluster_config[i].CREDENTIALS.get(),
  1602. }
  1603. )
  1604. )
  1605. return OrderedDict(clusters)
  1606. def has_multi_clusters():
  1607. '''If Hue is configured to talk to more than one completely independent clusters'''
  1608. return len(CLUSTERS.get()) > 1
  1609. def has_connectors():
  1610. '''When the connector feature is turned on'''
  1611. return ENABLE_CONNECTORS.get()
  1612. CLUSTERS = UnspecifiedConfigSection(
  1613. "clusters",
  1614. help="One entry for each additional remote cluster Hue can interact with.",
  1615. each=ConfigSection(
  1616. help=_("Id of the cluster."),
  1617. members=dict(
  1618. NAME=Config(
  1619. "name",
  1620. help=_("Nice name of the cluster to show to the user. Same as id if not specified."),
  1621. default=None,
  1622. type=str,
  1623. ),
  1624. TYPE=Config(
  1625. "type",
  1626. help=_("Type of cluster, e.g. plain, CM, Snowball..."),
  1627. default='direct',
  1628. type=str,
  1629. ),
  1630. CREDENTIALS=Config(
  1631. "credentials",
  1632. help=_("How to connect to the of remote cluster management system."),
  1633. default='{}',
  1634. type=coerce_json_dict,
  1635. )
  1636. )
  1637. )
  1638. )
  1639. ENABLE_CONNECTORS = Config(
  1640. key='enable_connectors',
  1641. default=False,
  1642. type=coerce_bool,
  1643. help=_('Turn on the Connector configuration and usage.')
  1644. )
  1645. CONNECTORS = UnspecifiedConfigSection(
  1646. key='connectors',
  1647. help=_("""Configuration options for connectors to external services"""),
  1648. each=ConfigSection(
  1649. help=_("Id of the connector."),
  1650. members=dict(
  1651. NICE_NAME=Config(
  1652. "nice_name",
  1653. help=_("Nice name of the connector to show to the user. Same as id if not specified."),
  1654. default=None,
  1655. type=str,
  1656. ),
  1657. DIALECT=Config(
  1658. "dialect",
  1659. help=_("The language or type of the integrated service. e.g. MySql, Hive, HDFS..."),
  1660. default=None,
  1661. type=str,
  1662. ),
  1663. INTERFACE=Config(
  1664. "interface",
  1665. help=_("The class of connector to use to connect to the service."),
  1666. default=None,
  1667. type=str,
  1668. ),
  1669. SETTINGS=Config(
  1670. "settings",
  1671. help=_("Json string of a list of name/value settings to configure the connector. e.g. '{\"name\": \"url\", \"value\": \"mysql://hue:hue@host:3306/hue\"}]'"),
  1672. default='{}',
  1673. type=coerce_json_dict,
  1674. ),
  1675. )
  1676. )
  1677. )
  1678. def validate_ldap(user, config):
  1679. res = []
  1680. if config.SEARCH_BIND_AUTHENTICATION.get():
  1681. if config.LDAP_URL.get() is not None:
  1682. bind_dn = config.BIND_DN.get()
  1683. bind_password = get_ldap_bind_password(config)
  1684. if bool(bind_dn) != bool(bind_password):
  1685. if bind_dn == None:
  1686. res.append((LDAP.BIND_DN,
  1687. new_str(_("If you set bind_password, then you must set bind_dn."))))
  1688. else:
  1689. res.append((LDAP.BIND_PASSWORD,
  1690. new_str(_("If you set bind_dn, then you must set bind_password."))))
  1691. else:
  1692. if config.NT_DOMAIN.get() is not None or \
  1693. config.LDAP_USERNAME_PATTERN.get() is not None:
  1694. if config.LDAP_URL.get() is None:
  1695. res.append((config.LDAP_URL,
  1696. new_str(_("LDAP is only partially configured. An LDAP URL must be provided."))))
  1697. if config.LDAP_URL.get() is not None:
  1698. if config.NT_DOMAIN.get() is None and \
  1699. config.LDAP_USERNAME_PATTERN.get() is None:
  1700. res.append((config.LDAP_URL,
  1701. new_str(_("LDAP is only partially configured. An NT Domain or username "
  1702. "search pattern must be provided."))))
  1703. if config.LDAP_USERNAME_PATTERN.get() is not None and \
  1704. '<username>' not in config.LDAP_USERNAME_PATTERN.get():
  1705. res.append((config.LDAP_USERNAME_PATTERN,
  1706. new_str(_("The LDAP username pattern should contain the special"
  1707. "<username> replacement string for authentication."))))
  1708. return res
  1709. def validate_database(user):
  1710. res = []
  1711. cursor = connection.cursor()
  1712. if connection.vendor == 'mysql':
  1713. try:
  1714. innodb_table_count = cursor.execute('''
  1715. SELECT *
  1716. FROM information_schema.tables
  1717. WHERE table_schema=DATABASE() AND engine = "innodb"''')
  1718. total_table_count = cursor.execute('''
  1719. SELECT *
  1720. FROM information_schema.tables
  1721. WHERE table_schema=DATABASE()''')
  1722. # Promote InnoDB storage engine
  1723. if innodb_table_count != total_table_count:
  1724. res.append(('PREFERRED_STORAGE_ENGINE', new_str(_('''We recommend MySQL InnoDB engine over
  1725. MyISAM which does not support transactions.'''))))
  1726. if innodb_table_count != 0 and innodb_table_count != total_table_count:
  1727. res.append(('MYSQL_STORAGE_ENGINE', new_str(_('''All tables in the database must be of the same
  1728. storage engine type (preferably InnoDB).'''))))
  1729. except Exception as ex:
  1730. LOG.exception("Error in config validation of MYSQL_STORAGE_ENGINE: %s", ex)
  1731. elif 'sqlite' in connection.vendor:
  1732. res.append(('SQLITE_NOT_FOR_PRODUCTION_USE', new_str(_('SQLite is only recommended for development environments. '
  1733. 'It might cause the "Database is locked" error. Migrating to MySQL, Oracle or PostgreSQL is strongly recommended.'))))
  1734. # Check if django_migrations table is up to date
  1735. try:
  1736. from desktop import appmanager
  1737. cursor.execute('''SELECT * from django_migrations''')
  1738. migration_history_entries = [(entry[1], entry[2]) for entry in cursor.fetchall()]
  1739. apps = appmanager.get_apps(user)
  1740. apps.append(appmanager.get_desktop_module('desktop'))
  1741. missing_migration_entries = []
  1742. for app in apps:
  1743. if app.migrations_path:
  1744. for migration_file_name in glob.iglob(app.migrations_path + '/*.py'):
  1745. migration_name = os.path.splitext(os.path.basename(migration_file_name))[0]
  1746. if migration_name != "__init__" and (app.name, migration_name) not in migration_history_entries:
  1747. missing_migration_entries.append((app.name, migration_name))
  1748. if missing_migration_entries:
  1749. res.append(('django_migrations', new_str(_('''django_migrations table seems to be corrupted or incomplete.
  1750. %s entries are missing in the table: %s''') % (len(missing_migration_entries), missing_migration_entries))))
  1751. except Exception:
  1752. LOG.exception("Error in config validation of django_migrations")
  1753. return res
  1754. def config_validator(user):
  1755. """
  1756. config_validator() -> [ (config_variable, error_message) ]
  1757. Called by core check_config() view.
  1758. """
  1759. from beeswax.models import QueryHistory, SavedQuery, Session
  1760. from desktop.lib import i18n
  1761. from desktop.models import Document, Document2 # Avoid cyclic loop
  1762. from desktop.settings import DOCUMENT2_MAX_ENTRIES # Avoid cyclic loop
  1763. from oozie.models import Job
  1764. res = []
  1765. doc_count = Document.objects.count()
  1766. if doc_count > DOCUMENT2_MAX_ENTRIES:
  1767. res.append(('DOCUMENT_CLEANUP_WARNING', new_str(_('Desktop Document has more than %d entries: %d, '
  1768. 'please run "hue desktop_document_cleanup --cm-managed" to remove old entries' % (DOCUMENT2_MAX_ENTRIES, doc_count)))))
  1769. doc2_count = Document2.objects.count()
  1770. if doc2_count > DOCUMENT2_MAX_ENTRIES:
  1771. res.append(('DOCUMENT2_CLEANUP_WARNING', new_str(_('Desktop Document2 has more than %d entries: %d, '
  1772. 'please run "hue desktop_document_cleanup --cm-managed" to remove old entries' % (DOCUMENT2_MAX_ENTRIES, doc2_count)))))
  1773. session_count = Session.objects.count()
  1774. if session_count > DOCUMENT2_MAX_ENTRIES:
  1775. res.append(('SESSION_CLEANUP_WARNING', new_str(_('Desktop Session has more than %d entries: %d, '
  1776. 'please run "hue desktop_document_cleanup --cm-managed" to remove old entries' % (DOCUMENT2_MAX_ENTRIES, session_count)))))
  1777. qh_count = QueryHistory.objects.count()
  1778. if qh_count > DOCUMENT2_MAX_ENTRIES:
  1779. res.append(('QueryHistory_CLEANUP_WARNING', new_str(_('Query History has more than %d entries: %d, '
  1780. 'please run "hue desktop_document_cleanup --cm-managed" to remove old entries' % (DOCUMENT2_MAX_ENTRIES, qh_count)))))
  1781. sq_count = SavedQuery.objects.count()
  1782. if sq_count > DOCUMENT2_MAX_ENTRIES:
  1783. res.append(('SavedQuery_CLEANUP_WARNING', new_str(_('Saved Query has more than %d entries: %d, '
  1784. 'please run "hue desktop_document_cleanup --cm-managed" to remove old entries' % (DOCUMENT2_MAX_ENTRIES, sq_count)))))
  1785. job_count = Job.objects.count()
  1786. if job_count > DOCUMENT2_MAX_ENTRIES:
  1787. res.append(('OOZIEJOB_CLEANUP_WARNING', new_str(_('Oozie Job has more than %d entries: %d, '
  1788. 'please run "hue desktop_document_cleanup --cm-managed" to remove old entries' % (DOCUMENT2_MAX_ENTRIES, job_count)))))
  1789. if not get_secret_key():
  1790. res.append((SECRET_KEY, new_str(_("Secret key should be configured as a random string. All sessions will be lost on restart"))))
  1791. # Validate SSL setup
  1792. if SSL_CERTIFICATE.get():
  1793. res.extend(validate_path(SSL_CERTIFICATE, is_dir=False))
  1794. if not SSL_PRIVATE_KEY.get():
  1795. res.append((SSL_PRIVATE_KEY, new_str(_("SSL private key file should be set to enable HTTPS."))))
  1796. else:
  1797. res.extend(validate_path(SSL_PRIVATE_KEY, is_dir=False))
  1798. # Validate encoding
  1799. if not i18n.validate_encoding(DEFAULT_SITE_ENCODING.get()):
  1800. res.append((DEFAULT_SITE_ENCODING, new_str(_("Encoding not supported."))))
  1801. # Validate kerberos
  1802. if KERBEROS.HUE_KEYTAB.get() is not None:
  1803. res.extend(validate_path(KERBEROS.HUE_KEYTAB, is_dir=False))
  1804. # Keytab should not be world or group accessible
  1805. kt_stat = os.stat(KERBEROS.HUE_KEYTAB.get())
  1806. if stat.S_IMODE(kt_stat.st_mode) & 0o077:
  1807. res.append((KERBEROS.HUE_KEYTAB,
  1808. force_unicode(_("Keytab should have 0600 permissions (has %o).") %
  1809. stat.S_IMODE(kt_stat.st_mode))))
  1810. res.extend(validate_path(KERBEROS.KINIT_PATH, is_dir=False))
  1811. res.extend(validate_path(KERBEROS.CCACHE_PATH, is_dir=False))
  1812. if LDAP.LDAP_SERVERS.get():
  1813. for ldap_record_key in LDAP.LDAP_SERVERS.get():
  1814. res.extend(validate_ldap(user, LDAP.LDAP_SERVERS.get()[ldap_record_key]))
  1815. else:
  1816. res.extend(validate_ldap(user, LDAP))
  1817. # Validate MYSQL storage engine of all tables
  1818. res.extend(validate_database(user))
  1819. # Validate if oozie email server is active
  1820. try:
  1821. from oozie.views.editor2 import _is_oozie_mail_enabled
  1822. if not _is_oozie_mail_enabled(user):
  1823. res.append(('OOZIE_EMAIL_SERVER', new_str(_('Email notifications is disabled for Workflows and Jobs as SMTP server is localhost.'))))
  1824. except Exception as e:
  1825. LOG.warn('Config check failed because Oozie app not installed %s' % e)
  1826. from notebook.models import make_notebook
  1827. from notebook.api import _save_notebook
  1828. notebook = make_notebook(name='test', editor_type='hive', statement='select "ทดสอบ"', status='ready')
  1829. notebook_doc = None
  1830. try:
  1831. notebook_doc, save_as = _save_notebook(notebook.get_data(), user)
  1832. except:
  1833. res.append(('DATABASE_CHARACTER_SET', new_str(_('Character set of <i>search</i> field in <i>desktop_document2</i> table is not UTF-8. <br>'
  1834. '<b>NOTE:</b> Configure the database for character set AL32UTF8 and national character set UTF8.'))))
  1835. if notebook_doc:
  1836. notebook_doc.delete()
  1837. if 'use_new_editor' in USE_NEW_EDITOR.bind_to:
  1838. res.append(('[desktop] use_new_editor', new_str(_('This configuration flag has been deprecated.'))))
  1839. return res
  1840. def get_redaction_policy():
  1841. """
  1842. Return the configured redaction policy.
  1843. """
  1844. return LOG_REDACTION_FILE.get()
  1845. def get_secret_key():
  1846. secret_key = os.environ.get('HUE_SECRET_KEY')
  1847. if secret_key is not None:
  1848. return secret_key
  1849. secret_key = SECRET_KEY.get()
  1850. if not secret_key:
  1851. secret_key = SECRET_KEY_SCRIPT.get()
  1852. return secret_key
  1853. def get_ssl_password():
  1854. password = os.environ.get('HUE_SSL_PASSWORD')
  1855. if password is not None:
  1856. return password
  1857. password = SSL_PASSWORD.get()
  1858. if not password:
  1859. password = SSL_PASSWORD_SCRIPT.get()
  1860. return password
  1861. def get_database_password():
  1862. password = os.environ.get('HUE_DATABASE_PASSWORD')
  1863. if password is not None:
  1864. return password
  1865. password = DATABASE.PASSWORD.get()
  1866. if not password:
  1867. password = DATABASE.PASSWORD_SCRIPT.get()
  1868. return password
  1869. def get_smtp_password():
  1870. password = os.environ.get('HUE_SMTP_PASSWORD')
  1871. if password is not None:
  1872. return password
  1873. password = SMTP.PASSWORD.get()
  1874. if not password:
  1875. password = SMTP.PASSWORD_SCRIPT.get()
  1876. return password
  1877. def get_ldap_bind_password(ldap_config):
  1878. password = os.environ.get('HUE_LDAP_BIND_PASSWORD')
  1879. if password is not None:
  1880. return password
  1881. password = ldap_config.BIND_PASSWORD.get()
  1882. if not password:
  1883. password = ldap_config.BIND_PASSWORD_SCRIPT.get()
  1884. return password
  1885. PERMISSION_ACTION_GS = "gs_access"
  1886. GC_ACCOUNTS = UnspecifiedConfigSection(
  1887. 'gc_accounts',
  1888. help=_('One entry for each GC account'),
  1889. each=ConfigSection(
  1890. help=_('Information about single GC account'),
  1891. members=dict(
  1892. JSON_CREDENTIALS=Config(
  1893. key='json_credentials',
  1894. type=str,
  1895. default=None,
  1896. )
  1897. )
  1898. )
  1899. )
  1900. def is_gs_enabled():
  1901. from desktop.lib.idbroker import conf as conf_idbroker # Circular dependencies desktop.conf -> idbroker.conf -> desktop.conf
  1902. return ('default' in list(GC_ACCOUNTS.keys()) and GC_ACCOUNTS['default'].JSON_CREDENTIALS.get()) or conf_idbroker.is_idbroker_enabled('gs')
  1903. def has_gs_access(user):
  1904. from desktop.auth.backend import is_admin
  1905. return user.is_authenticated() and user.is_active and (is_admin(user) or user.has_hue_permission(action="gs_access", app="filebrowser"))