]>
git.proxmox.com Git - ceph.git/blob - ceph/src/pybind/mgr/dashboard/services/auth.py
1 # -*- coding: utf-8 -*-
2 from __future__
import absolute_import
4 from base64
import b64encode
15 from .access_control
import LocalAuthenticator
, UserDoesNotExist
18 cherrypy
.config
.update({
19 'response.headers.server': 'Ceph-Dashboard',
20 'response.headers.content-security-policy': "frame-ancestors 'self';",
21 'response.headers.x-content-type-options': 'nosniff',
22 'response.headers.strict-transport-security': 'max-age=63072000; includeSubDomains; preload'
26 class JwtManager(object):
27 JWT_TOKEN_BLACKLIST_KEY
= "jwt_token_black_list"
28 JWT_TOKEN_TTL
= 28800 # default 8 hours
29 JWT_ALGORITHM
= 'HS256'
32 LOCAL_USER
= threading
.local()
36 secret
= os
.urandom(16)
37 return b64encode(secret
).decode('utf-8')
41 cls
.logger
= logging
.getLogger('jwt') # type: ignore
42 # generate a new secret if it does not exist
43 secret
= mgr
.get_store('jwt_secret')
45 secret
= cls
._gen
_secret
()
46 mgr
.set_store('jwt_secret', secret
)
50 def gen_token(cls
, username
):
53 ttl
= mgr
.get_module_option('jwt_token_ttl', cls
.JWT_TOKEN_TTL
)
55 now
= int(time
.time())
57 'iss': 'ceph-dashboard',
58 'jti': str(uuid
.uuid4()),
63 return jwt
.encode(payload
, cls
._secret
, algorithm
=cls
.JWT_ALGORITHM
) # type: ignore
66 def decode_token(cls
, token
):
69 return jwt
.decode(token
, cls
._secret
, algorithms
=cls
.JWT_ALGORITHM
) # type: ignore
72 def get_token_from_header(cls
):
73 auth_cookie_name
= 'token'
76 return cherrypy
.request
.cookie
[auth_cookie_name
].value
79 # fall-back: use Authorization header
80 auth_header
= cherrypy
.request
.headers
.get('authorization')
81 if auth_header
is not None:
82 scheme
, params
= auth_header
.split(' ', 1)
83 if scheme
.lower() == 'bearer':
89 def set_user(cls
, username
):
90 cls
.LOCAL_USER
.username
= username
97 def get_username(cls
):
98 return getattr(cls
.LOCAL_USER
, 'username', None)
101 def get_user(cls
, token
):
103 dtoken
= JwtManager
.decode_token(token
)
104 if not JwtManager
.is_blacklisted(dtoken
['jti']):
105 user
= AuthManager
.get_user(dtoken
['username'])
106 if user
.last_update
<= dtoken
['iat']:
108 cls
.logger
.debug( # type: ignore
109 "user info changed after token was issued, iat=%s last_update=%s",
110 dtoken
['iat'], user
.last_update
113 cls
.logger
.debug('Token is black-listed') # type: ignore
114 except jwt
.ExpiredSignatureError
:
115 cls
.logger
.debug("Token has expired") # type: ignore
116 except jwt
.InvalidTokenError
:
117 cls
.logger
.debug("Failed to decode token") # type: ignore
118 except UserDoesNotExist
:
119 cls
.logger
.debug( # type: ignore
120 "Invalid token: user %s does not exist", dtoken
['username']
125 def blacklist_token(cls
, token
):
126 token
= cls
.decode_token(token
)
127 blacklist_json
= mgr
.get_store(cls
.JWT_TOKEN_BLACKLIST_KEY
)
128 if not blacklist_json
:
129 blacklist_json
= "{}"
130 bl_dict
= json
.loads(blacklist_json
)
133 # remove expired tokens
135 for jti
, exp
in bl_dict
.items():
137 to_delete
.append(jti
)
138 for jti
in to_delete
:
141 bl_dict
[token
['jti']] = token
['exp']
142 mgr
.set_store(cls
.JWT_TOKEN_BLACKLIST_KEY
, json
.dumps(bl_dict
))
145 def is_blacklisted(cls
, jti
):
146 blacklist_json
= mgr
.get_store(cls
.JWT_TOKEN_BLACKLIST_KEY
)
147 if not blacklist_json
:
148 blacklist_json
= "{}"
149 bl_dict
= json
.loads(blacklist_json
)
150 return jti
in bl_dict
153 class AuthManager(object):
158 cls
.AUTH_PROVIDER
= LocalAuthenticator()
161 def get_user(cls
, username
):
162 return cls
.AUTH_PROVIDER
.get_user(username
) # type: ignore
165 def authenticate(cls
, username
, password
):
166 return cls
.AUTH_PROVIDER
.authenticate(username
, password
) # type: ignore
169 def authorize(cls
, username
, scope
, permissions
):
170 return cls
.AUTH_PROVIDER
.authorize(username
, scope
, permissions
) # type: ignore
173 class AuthManagerTool(cherrypy
.Tool
):
175 super(AuthManagerTool
, self
).__init
__(
176 'before_handler', self
._check
_authentication
, priority
=20)
177 self
.logger
= logging
.getLogger('auth')
179 def _check_authentication(self
):
180 JwtManager
.reset_user()
181 token
= JwtManager
.get_token_from_header()
182 self
.logger
.debug("token: %s", token
)
184 user
= JwtManager
.get_user(token
)
186 self
._check
_authorization
(user
.username
)
188 self
.logger
.debug('Unauthorized access to %s',
189 cherrypy
.url(relative
='server'))
190 raise cherrypy
.HTTPError(401, 'You are not authorized to access '
193 def _check_authorization(self
, username
):
194 self
.logger
.debug("checking authorization...")
196 handler
= cherrypy
.request
.handler
.callable
197 controller
= handler
.__self
__
198 sec_scope
= getattr(controller
, '_security_scope', None)
199 sec_perms
= getattr(handler
, '_security_permissions', None)
200 JwtManager
.set_user(username
)
203 # controller does not define any authorization restrictions
206 self
.logger
.debug("checking '%s' access to '%s' scope", sec_perms
,
210 self
.logger
.debug("Fail to check permission on: %s:%s", controller
,
212 raise cherrypy
.HTTPError(403, "You don't have permissions to "
213 "access that resource")
215 if not AuthManager
.authorize(username
, sec_scope
, sec_perms
):
216 raise cherrypy
.HTTPError(403, "You don't have permissions to "
217 "access that resource")