2 # -*- encoding: utf-8; py-indent-offset: 4 -*-
3 # +------------------------------------------------------------------+
4 # | ____ _ _ __ __ _ __ |
5 # | / ___| |__ ___ ___| | __ | \/ | |/ / |
6 # | | | | '_ \ / _ \/ __| |/ / | |\/| | ' / |
7 # | | |___| | | | __/ (__| < | | | | . \ |
8 # | \____|_| |_|\___|\___|_|\_\___|_| |_|_|\_\ |
10 # | Copyright Mathias Kettner 2014 mk@mathias-kettner.de |
11 # +------------------------------------------------------------------+
13 # This file is part of Check_MK.
14 # The official homepage is at http://mathias-kettner.de/check_mk.
16 # check_mk is free software; you can redistribute it and/or modify it
17 # under the terms of the GNU General Public License as published by
18 # the Free Software Foundation in version 2. check_mk is distributed
19 # in the hope that it will be useful, but WITHOUT ANY WARRANTY; with-
20 # out even the implied warranty of MERCHANTABILITY or FITNESS FOR A
21 # PARTICULAR PURPOSE. See the GNU General Public License for more de-
22 # tails. You should have received a copy of the GNU General Public
23 # License along with GNU Make; see the file COPYING. If not, write
24 # to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
25 # Boston, MA 02110-1301 USA.
33 import cmk
.utils
.paths
34 import cmk
.utils
.store
as store
35 import cmk
.utils
.profile
38 import cmk
.gui
.sites
as sites
39 import cmk
.gui
.config
as config
40 import cmk
.gui
.modules
as modules
41 import cmk
.gui
.pages
as pages
42 import cmk
.gui
.userdb
as userdb
43 import cmk
.gui
.login
as login
44 import cmk
.gui
.log
as log
45 import cmk
.gui
.htmllib
47 import cmk
.gui
.globals
48 from cmk
.gui
.log
import logger
49 from cmk
.gui
.i18n
import _
50 from cmk
.gui
.globals import html
52 from cmk
.gui
.exceptions
import (
57 MKUnauthenticatedException
,
63 class Application(object):
64 """The Check_MK GUI WSGI entry point"""
66 def __init__(self
, environ
, start_response
):
67 self
._environ
= environ
68 self
._start
_response
= start_response
69 self
._request
= cmk
.gui
.http
.Request(environ
)
70 self
._response
= cmk
.gui
.http
.Response(is_secure
=self
._request
.is_ssl_request
)
72 # Register application object for access from other modules
73 cmk
.gui
.globals.current_app
.set_current(self
)
75 # Initialize some simple per request global namespace. Nothing fancy here.
76 # Each request starts with a fresh instance.
79 # Create an object that contains all data about the request and
80 # helper functions for creating valid HTML. Parse URI and
81 # store results in the request object for later usage.
83 h
= cmk
.gui
.htmllib
.html(self
._request
, self
._response
)
84 cmk
.gui
.globals.html
.set_current(h
)
86 logger
.exception("Failed to process request")
87 self
._response
.headers
["Content-type"] = "text/plain; charset=UTF-8"
88 self
._response
.stream
.write(
89 "Failed to process request. Have a look at 'var/log/web.log' for more information.\n"
93 self
._process
_request
()
95 def _process_request(self
):
99 with cmk
.utils
.profile
.Profile(
100 enabled
=self
._profiling
_enabled
(),
101 profile_file
=os
.path
.join(cmk
.utils
.paths
.var_dir
, "multisite.profile")):
102 self
._handle
_request
()
104 except HTTPRedirect
, e
:
105 self
._response
.status_code
= e
.status
106 self
._response
.headers
["Location"] = e
.url
108 except FinalizeRequest
, e
:
109 self
._response
.status_code
= e
.status
114 MKUnauthenticatedException
,
117 livestatus
.MKLivestatusNotFoundError
,
118 livestatus
.MKLivestatusException
,
120 # TODO: Refactor all the special cases handled here to simplify the exception handling
122 if ty
== livestatus
.MKLivestatusNotFoundError
:
123 title
= _("Data not found")
124 plain_title
= _("Livestatus-data not found")
125 elif isinstance(e
, livestatus
.MKLivestatusException
):
126 title
= _("Livestatus problem")
127 plain_title
= _("Livestatus problem")
130 plain_title
= e
.plain_title()
132 if self
._plain
_error
():
133 html
.set_output_format("text")
134 html
.write("%s: %s\n" % (plain_title
, e
))
135 elif not self
._fail
_silently
():
140 # Some exception need to set a specific HTTP status code
141 if ty
== MKUnauthenticatedException
:
142 self
._response
.status_code
= httplib
.UNAUTHORIZED
143 elif ty
== livestatus
.MKLivestatusException
:
144 self
._response
.status_code
= httplib
.BAD_GATEWAY
146 if ty
in [MKConfigError
, MKGeneralException
]:
147 logger
.error(_("%s: %s") % (plain_title
, e
))
151 if self
._plain
_error
():
152 html
.set_output_format("text")
153 html
.write(_("Internal error") + ": %s\n" % e
)
154 elif not self
._fail
_silently
():
155 crash_handler
= pages
.get_page_handler("gui_crash")
156 if not crash_handler
:
168 """Final steps that are performed after each handled HTTP request"""
169 store
.release_all_locks()
173 cmk
.gui
.globals.html
.unset_current()
175 def _handle_request(self
):
178 # Make sure all plugins are avaiable as early as possible. At least
179 # we need the plugins (i.e. the permissions declared in these) at the
180 # time before the first login for generating auth.php.
181 modules
.load_all_plugins()
183 # Clean up left over livestatus + connection objects (which are
184 # globally stored in sites module). This is a quick fix for the 1.5
185 # and will be cleaned up in 1.6 by storing these objects in the user
186 # request context instead of a global context.
189 handler
= pages
.get_page_handler(html
.myfile
, self
._page
_not
_found
)
191 # Some pages do skip authentication. This is done by adding
192 # noauth: to the page hander, e.g. "noauth:run_cron" : ...
193 # TODO: Eliminate those "noauth:" pages. Eventually replace it by call using
194 # the now existing default automation user.
195 if handler
== self
._page
_not
_found
:
196 handler
= pages
.get_page_handler("noauth:" + html
.myfile
, self
._page
_not
_found
)
197 if handler
!= self
._page
_not
_found
:
201 self
._show
_exception
_info
(e
)
202 raise FinalizeRequest(httplib
.OK
)
204 # Ensure the user is authenticated. This call is wrapping all the different
205 # authentication modes the Check_MK GUI supports and initializes the logged
207 if not login
.authenticate(self
._request
):
208 self
._handle
_not
_authenticated
()
210 # Initialize the multiste cmk.gui.i18n. This will be replaced by
211 # language settings stored in the user profile after the user
212 # has been initialized
213 self
._localize
_request
()
215 # Update the UI theme with the attribute configured by the user
216 html
.set_theme(config
.user
.get_attribute("ui_theme"))
218 self
._ensure
_general
_access
()
221 def _show_exception_info(self
, e
):
222 html
.write_text("%s" % e
)
224 html
.write_text(traceback
.format_exc())
226 def _handle_not_authenticated(self
):
227 if self
._fail
_silently
():
228 # While api call don't show the login dialog
229 raise MKUnauthenticatedException(_('You are not authenticated.'))
231 # Redirect to the login-dialog with the current url as original target
232 # Never render the login form directly when accessing urls like "index.py"
233 # or "dashboard.py". This results in strange problems.
234 if html
.myfile
!= 'login':
235 raise HTTPRedirect('%scheck_mk/login.py?_origtarget=%s' %
236 (config
.url_prefix(), html
.urlencode(html
.makeuri([]))))
238 # This either displays the login page or validates the information submitted
239 # to the login form. After successful login a http redirect to the originally
240 # requested page is performed.
241 login_page
= login
.LoginPage()
242 login_page
.set_no_html_output(self
._plain
_error
())
243 login_page
.handle_page()
245 raise FinalizeRequest(httplib
.OK
)
247 def _localize_request(self
):
248 previous_language
= cmk
.gui
.i18n
.get_current_language()
249 user_language
= html
.get_ascii_input("lang", config
.user
.language())
251 html
.set_language_cookie(user_language
)
252 cmk
.gui
.i18n
.localize(user_language
)
254 # All plugins might have to be reloaded due to a language change. Only trigger
255 # a second plugin loading when the user is really using a custom localized GUI.
256 # Otherwise the load_all_plugins() at the beginning of the request is sufficient.
257 if cmk
.gui
.i18n
.get_current_language() != previous_language
:
258 modules
.load_all_plugins()
260 def _fail_silently(self
):
261 """Ajax-Functions want no HTML output in case of an error but
262 just a plain server result code of 500"""
263 return html
.request
.has_var("_ajaxid")
265 def _plain_error(self
):
266 """Webservice functions may decide to get a normal result code
267 but a text with an error message in case of an error"""
268 return html
.request
.has_var("_plain_error") or html
.myfile
== "webapi"
270 def _profiling_enabled(self
):
271 if config
.profile
is False:
272 return False # Not enabled
274 if config
.profile
== "enable_by_var" and not html
.request
.has_var("_profile"):
275 return False # Not enabled by HTTP variable
279 # TODO: This is a page handler. It should not be located in generic application
280 # object. Move it to another place
281 def _page_not_found(self
):
282 if html
.request
.has_var("_plain_error"):
283 html
.write(_("Page not found"))
285 html
.header(_("Page not found"))
286 html
.show_error(_("This page was not found. Sorry."))
289 def _ensure_general_access(self
):
290 if config
.user
.may("general.use"):
294 _("You are not authorized to use the Check_MK GUI. Sorry. "
295 "You are logged in as <b>%s</b>.") % config
.user
.id
298 if config
.user
.role_ids
:
299 reason
.append(_("Your roles are <b>%s</b>.") % ", ".join(config
.user
.role_ids
))
301 reason
.append(_("<b>You do not have any roles.</b>"))
304 _("If you think this is an error, please ask your administrator "
305 "to check the permissions configuration."))
307 if login
.auth_type
== 'cookie':
309 _("<p>You have been logged out. Please reload the page "
310 "to re-authenticate.</p>"))
311 login
.del_auth_cookie()
313 raise MKAuthException(" ".join(reason
))
316 """Is called by the WSGI server to serve the current page"""
317 return self
._response
(self
._environ
, self
._start
_response
)
320 # Early initialization upon first start of the application by the server
323 modules
.init_modules()
326 # Run the global application initialization code here. It is called
327 # only once during the startup of the application server.