]>
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
19 class JwtManager(object):
20 JWT_TOKEN_BLACKLIST_KEY
= "jwt_token_black_list"
21 JWT_TOKEN_TTL
= 28800 # default 8 hours
22 JWT_ALGORITHM
= 'HS256'
25 LOCAL_USER
= threading
.local()
29 secret
= os
.urandom(16)
30 return b64encode(secret
).decode('utf-8')
34 cls
.logger
= logging
.getLogger('jwt') # type: ignore
35 # generate a new secret if it does not exist
36 secret
= mgr
.get_store('jwt_secret')
38 secret
= cls
._gen
_secret
()
39 mgr
.set_store('jwt_secret', secret
)
43 def gen_token(cls
, username
):
46 ttl
= mgr
.get_module_option('jwt_token_ttl', cls
.JWT_TOKEN_TTL
)
48 now
= int(time
.time())
50 'iss': 'ceph-dashboard',
51 'jti': str(uuid
.uuid4()),
56 return jwt
.encode(payload
, cls
._secret
, algorithm
=cls
.JWT_ALGORITHM
) # type: ignore
59 def decode_token(cls
, token
):
62 return jwt
.decode(token
, cls
._secret
, algorithms
=cls
.JWT_ALGORITHM
) # type: ignore
65 def get_token_from_header(cls
):
66 auth_cookie_name
= 'token'
69 return cherrypy
.request
.cookie
[auth_cookie_name
].value
72 # fall-back: use Authorization header
73 auth_header
= cherrypy
.request
.headers
.get('authorization')
74 if auth_header
is not None:
75 scheme
, params
= auth_header
.split(' ', 1)
76 if scheme
.lower() == 'bearer':
82 def set_user(cls
, username
):
83 cls
.LOCAL_USER
.username
= username
90 def get_username(cls
):
91 return getattr(cls
.LOCAL_USER
, 'username', None)
94 def get_user(cls
, token
):
96 dtoken
= JwtManager
.decode_token(token
)
97 if not JwtManager
.is_blacklisted(dtoken
['jti']):
98 user
= AuthManager
.get_user(dtoken
['username'])
99 if user
.last_update
<= dtoken
['iat']:
101 cls
.logger
.debug( # type: ignore
102 "user info changed after token was issued, iat=%s last_update=%s",
103 dtoken
['iat'], user
.last_update
106 cls
.logger
.debug('Token is black-listed') # type: ignore
107 except jwt
.ExpiredSignatureError
:
108 cls
.logger
.debug("Token has expired") # type: ignore
109 except jwt
.InvalidTokenError
:
110 cls
.logger
.debug("Failed to decode token") # type: ignore
111 except UserDoesNotExist
:
112 cls
.logger
.debug( # type: ignore
113 "Invalid token: user %s does not exist", dtoken
['username']
118 def blacklist_token(cls
, token
):
119 token
= jwt
.decode(token
, verify
=False)
120 blacklist_json
= mgr
.get_store(cls
.JWT_TOKEN_BLACKLIST_KEY
)
121 if not blacklist_json
:
122 blacklist_json
= "{}"
123 bl_dict
= json
.loads(blacklist_json
)
126 # remove expired tokens
128 for jti
, exp
in bl_dict
.items():
130 to_delete
.append(jti
)
131 for jti
in to_delete
:
134 bl_dict
[token
['jti']] = token
['exp']
135 mgr
.set_store(cls
.JWT_TOKEN_BLACKLIST_KEY
, json
.dumps(bl_dict
))
138 def is_blacklisted(cls
, jti
):
139 blacklist_json
= mgr
.get_store(cls
.JWT_TOKEN_BLACKLIST_KEY
)
140 if not blacklist_json
:
141 blacklist_json
= "{}"
142 bl_dict
= json
.loads(blacklist_json
)
143 return jti
in bl_dict
146 class AuthManager(object):
151 cls
.AUTH_PROVIDER
= LocalAuthenticator()
154 def get_user(cls
, username
):
155 return cls
.AUTH_PROVIDER
.get_user(username
) # type: ignore
158 def authenticate(cls
, username
, password
):
159 return cls
.AUTH_PROVIDER
.authenticate(username
, password
) # type: ignore
162 def authorize(cls
, username
, scope
, permissions
):
163 return cls
.AUTH_PROVIDER
.authorize(username
, scope
, permissions
) # type: ignore
166 class AuthManagerTool(cherrypy
.Tool
):
168 super(AuthManagerTool
, self
).__init
__(
169 'before_handler', self
._check
_authentication
, priority
=20)
170 self
.logger
= logging
.getLogger('auth')
172 def _check_authentication(self
):
173 JwtManager
.reset_user()
174 token
= JwtManager
.get_token_from_header()
175 self
.logger
.debug("token: %s", token
)
177 user
= JwtManager
.get_user(token
)
179 self
._check
_authorization
(user
.username
)
181 self
.logger
.debug('Unauthorized access to %s',
182 cherrypy
.url(relative
='server'))
183 raise cherrypy
.HTTPError(401, 'You are not authorized to access '
186 def _check_authorization(self
, username
):
187 self
.logger
.debug("checking authorization...")
189 handler
= cherrypy
.request
.handler
.callable
190 controller
= handler
.__self
__
191 sec_scope
= getattr(controller
, '_security_scope', None)
192 sec_perms
= getattr(handler
, '_security_permissions', None)
193 JwtManager
.set_user(username
)
196 # controller does not define any authorization restrictions
199 self
.logger
.debug("checking '%s' access to '%s' scope", sec_perms
,
203 self
.logger
.debug("Fail to check permission on: %s:%s", controller
,
205 raise cherrypy
.HTTPError(403, "You don't have permissions to "
206 "access that resource")
208 if not AuthManager
.authorize(username
, sec_scope
, sec_perms
):
209 raise cherrypy
.HTTPError(403, "You don't have permissions to "
210 "access that resource")