]>
git.proxmox.com Git - ceph.git/blob - ceph/src/pybind/mgr/dashboard/controllers/home.py
1 # -*- coding: utf-8 -*-
2 from __future__
import absolute_import
9 from functools
import lru_cache
11 from ..plugins
.lru_cache
import lru_cache
14 from cherrypy
.lib
.static
import serve_file
16 from . import Controller
, UiApiController
, BaseController
, Proxy
, Endpoint
20 logger
= logging
.getLogger("controllers.home")
23 class LanguageMixin(object):
27 for f
in os
.listdir(mgr
.get_frontend_path())
28 if os
.path
.isdir(os
.path
.join(mgr
.get_frontend_path(), f
))
30 self
.LANGUAGES_PATH_MAP
= {
33 'path': os
.path
.join(mgr
.get_frontend_path(), f
)
35 for f
in self
.LANGUAGES
37 # pre-populating with the primary language subtag.
38 for lang
in list(self
.LANGUAGES_PATH_MAP
.keys()):
40 self
.LANGUAGES_PATH_MAP
[lang
.split('-')[0]] = {
41 'lang': self
.LANGUAGES_PATH_MAP
[lang
]['lang'],
42 'path': self
.LANGUAGES_PATH_MAP
[lang
]['path']
44 with
open("{}/../package.json".format(mgr
.get_frontend_path()),
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
__()
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
61 def _parse_accept_language(self
, accept_lang_header
):
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")
68 tag_match
= self
.LANG_TAG_RE
.match(m
.group(1))
70 raise cherrypy
.HTTPError(400, "Malformed 'Accept-Language' header")
71 locale
= tag_match
.group('locale').lower()
72 weight
= tag_match
.group('weight')
77 raise cherrypy
.HTTPError(400, "Malformed 'Accept-Language' header")
80 result
.append((locale
, ratio
))
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
]
87 def _language_dir(self
, 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']
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
101 def __call__(self
, path
, **params
):
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
)
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
)
113 langs
= [self
.DEFAULT_LANGUAGE
.lower()]
114 logger
.debug("frontend language from headers: %s", langs
)
116 base_dir
= self
._language
_dir
(langs
)
117 full_path
= os
.path
.join(base_dir
, path
)
119 # Block uplevel attacks
120 if not os
.path
.normpath(full_path
).startswith(os
.path
.normpath(base_dir
)):
121 raise cherrypy
.HTTPError(403) # Forbidden
123 logger
.debug("serving static content: %s", full_path
)
124 if 'Vary' in cherrypy
.response
.headers
:
125 cherrypy
.response
.headers
['Vary'] = "{}, Accept-Language"
127 cherrypy
.response
.headers
['Vary'] = "Accept-Language"
129 cherrypy
.response
.headers
['Cache-control'] = "no-cache"
130 return serve_file(full_path
)
133 @UiApiController("/langs", secure
=False)
134 class LangsController(BaseController
, LanguageMixin
):
137 return list(self
.LANGUAGES
)