]> git.proxmox.com Git - ceph.git/blob - ceph/src/pybind/mgr/dashboard/controllers/home.py
import 15.2.9
[ceph.git] / ceph / src / pybind / mgr / dashboard / controllers / home.py
1 # -*- coding: utf-8 -*-
2 from __future__ import absolute_import
3
4 import os
5 import re
6 import json
7 import logging
8 try:
9 from functools import lru_cache
10 except ImportError:
11 from ..plugins.lru_cache import lru_cache
12
13 import cherrypy
14 from cherrypy.lib.static import serve_file
15
16 from . import Controller, UiApiController, BaseController, Proxy, Endpoint
17 from .. import mgr
18
19
20 logger = logging.getLogger("controllers.home")
21
22
23 class LanguageMixin(object):
24 def __init__(self):
25 self.LANGUAGES = {
26 f
27 for f in os.listdir(mgr.get_frontend_path())
28 if os.path.isdir(os.path.join(mgr.get_frontend_path(), f))
29 }
30 self.LANGUAGES_PATH_MAP = {
31 f.lower(): {
32 'lang': f,
33 'path': os.path.join(mgr.get_frontend_path(), f)
34 }
35 for f in self.LANGUAGES
36 }
37 # pre-populating with the primary language subtag.
38 for lang in list(self.LANGUAGES_PATH_MAP.keys()):
39 if '-' in lang:
40 self.LANGUAGES_PATH_MAP[lang.split('-')[0]] = {
41 'lang': self.LANGUAGES_PATH_MAP[lang]['lang'],
42 'path': self.LANGUAGES_PATH_MAP[lang]['path']
43 }
44 with open("{}/../package.json".format(mgr.get_frontend_path()),
45 "r") as f:
46 config = json.load(f)
47 self.DEFAULT_LANGUAGE = config['config']['locale']
48 self.DEFAULT_LANGUAGE_PATH = os.path.join(mgr.get_frontend_path(),
49 self.DEFAULT_LANGUAGE)
50 super(LanguageMixin, self).__init__()
51
52
53 @Controller("/", secure=False)
54 class HomeController(BaseController, LanguageMixin):
55 LANG_TAG_SEQ_RE = re.compile(r'\s*([^,]+)\s*,?\s*')
56 LANG_TAG_RE = re.compile(
57 r'^(?P<locale>[a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})?)(;q=(?P<weight>[01]\.\d{0,3}))?$')
58 MAX_ACCEPTED_LANGS = 10
59
60 @lru_cache()
61 def _parse_accept_language(self, accept_lang_header):
62 result = []
63 for i, m in enumerate(self.LANG_TAG_SEQ_RE.finditer(accept_lang_header)):
64 if i >= self.MAX_ACCEPTED_LANGS:
65 logger.debug("reached max accepted languages, skipping remaining")
66 break
67
68 tag_match = self.LANG_TAG_RE.match(m.group(1))
69 if tag_match is None:
70 raise cherrypy.HTTPError(400, "Malformed 'Accept-Language' header")
71 locale = tag_match.group('locale').lower()
72 weight = tag_match.group('weight')
73 if weight:
74 try:
75 ratio = float(weight)
76 except ValueError:
77 raise cherrypy.HTTPError(400, "Malformed 'Accept-Language' header")
78 else:
79 ratio = 1.0
80 result.append((locale, ratio))
81
82 result.sort(key=lambda l: l[0])
83 result.sort(key=lambda l: l[1], reverse=True)
84 logger.debug("language preference: %s", result)
85 return [l[0] for l in result]
86
87 def _language_dir(self, langs):
88 for lang in langs:
89 if lang in self.LANGUAGES_PATH_MAP:
90 logger.debug("found directory for language '%s'", lang)
91 cherrypy.response.headers[
92 'Content-Language'] = self.LANGUAGES_PATH_MAP[lang]['lang']
93 return self.LANGUAGES_PATH_MAP[lang]['path']
94
95 logger.debug("Languages '%s' not available, falling back to %s",
96 langs, self.DEFAULT_LANGUAGE)
97 cherrypy.response.headers['Content-Language'] = self.DEFAULT_LANGUAGE
98 return self.DEFAULT_LANGUAGE_PATH
99
100 @Proxy()
101 def __call__(self, path, **params):
102 if not path:
103 path = "index.html"
104
105 if 'cd-lang' in cherrypy.request.cookie:
106 langs = [cherrypy.request.cookie['cd-lang'].value.lower()]
107 logger.debug("frontend language from cookie: %s", langs)
108 else:
109 if 'Accept-Language' in cherrypy.request.headers:
110 accept_lang_header = cherrypy.request.headers['Accept-Language']
111 langs = self._parse_accept_language(accept_lang_header)
112 else:
113 langs = [self.DEFAULT_LANGUAGE.lower()]
114 logger.debug("frontend language from headers: %s", langs)
115
116 base_dir = self._language_dir(langs)
117 full_path = os.path.join(base_dir, path)
118
119 # Block uplevel attacks
120 if not os.path.normpath(full_path).startswith(os.path.normpath(base_dir)):
121 raise cherrypy.HTTPError(403) # Forbidden
122
123 logger.debug("serving static content: %s", full_path)
124 if 'Vary' in cherrypy.response.headers:
125 cherrypy.response.headers['Vary'] = "{}, Accept-Language"
126 else:
127 cherrypy.response.headers['Vary'] = "Accept-Language"
128
129 cherrypy.response.headers['Cache-control'] = "no-cache"
130 return serve_file(full_path)
131
132
133 @UiApiController("/langs", secure=False)
134 class LangsController(BaseController, LanguageMixin):
135 @Endpoint('GET')
136 def __call__(self):
137 return list(self.LANGUAGES)