|
13 | 13 | # 6. Interface to verify tokens
|
14 | 14 |
|
15 | 15 | import getpass
|
| 16 | +import hashlib |
16 | 17 | import logging
|
| 18 | +import os |
17 | 19 | import random
|
18 | 20 | import time
|
19 | 21 | from collections.abc import Iterable, Mapping
|
20 | 22 |
|
| 23 | +import salt.cache |
21 | 24 | import salt.channel.client
|
22 |
| -import salt.config |
23 | 25 | import salt.exceptions
|
24 | 26 | import salt.loader
|
25 |
| -import salt.payload |
26 | 27 | import salt.utils.args
|
27 |
| -import salt.utils.dictupdate |
28 | 28 | import salt.utils.files
|
29 | 29 | import salt.utils.minions
|
30 | 30 | import salt.utils.network
|
@@ -60,6 +60,7 @@ def __init__(self, opts, ckminions=None):
|
60 | 60 | self.max_fail = 1.0
|
61 | 61 | self.auth = salt.loader.auth(opts)
|
62 | 62 | self.tokens = salt.loader.eauth_tokens(opts)
|
| 63 | + self.cache = salt.cache.factory(opts, driver=opts["eauth_tokens.cache_driver"]) |
63 | 64 | self.ckminions = ckminions or salt.utils.minions.CkMinions(opts)
|
64 | 65 |
|
65 | 66 | def load_name(self, load):
|
@@ -230,54 +231,168 @@ def mk_token(self, load):
|
230 | 231 | if groups:
|
231 | 232 | tdata["groups"] = groups
|
232 | 233 |
|
233 |
| - return self.tokens["{}.mk_token".format(self.opts["eauth_tokens"])]( |
234 |
| - self.opts, tdata |
235 |
| - ) |
| 234 | + if self.opts["eauth_tokens.cache_driver"] == "rediscluster": |
| 235 | + salt.utils.versions.warn_until( |
| 236 | + 3009, |
| 237 | + "The 'rediscluster' token backend has been deprecated, and will be removed " |
| 238 | + "in the Potassium release. Please use the 'redis_cache' cache backend instead.", |
| 239 | + ) |
| 240 | + return self.tokens["{}.mk_token".format(self.opts["eauth_tokens"])]( |
| 241 | + self.opts, tdata |
| 242 | + ) |
| 243 | + else: |
| 244 | + hash_type = getattr(hashlib, self.opts.get("hash_type", "md5")) |
| 245 | + new_token = str(hash_type(os.urandom(512)).hexdigest()) |
| 246 | + tdata["token"] = new_token |
| 247 | + try: |
| 248 | + self.cache.store("tokens", new_token, tdata, expires=tdata["expire"]) |
| 249 | + except salt.exceptions.SaltCacheError as err: |
| 250 | + log.error( |
| 251 | + "Cannot mk_token from tokens cache using %s: %s", |
| 252 | + self.opts["eauth_tokens.cache_driver"], |
| 253 | + err, |
| 254 | + ) |
| 255 | + return {} |
| 256 | + |
| 257 | + return tdata |
236 | 258 |
|
237 | 259 | def get_tok(self, tok):
|
238 | 260 | """
|
239 | 261 | Return the name associated with the token, or False if the token is
|
240 | 262 | not valid
|
241 | 263 | """
|
242 |
| - tdata = {} |
243 |
| - try: |
244 |
| - tdata = self.tokens["{}.get_token".format(self.opts["eauth_tokens"])]( |
245 |
| - self.opts, tok |
| 264 | + if self.opts["eauth_tokens.cache_driver"] == "rediscluster": |
| 265 | + salt.utils.versions.warn_until( |
| 266 | + 3009, |
| 267 | + "The 'rediscluster' token backend has been deprecated, and will be removed " |
| 268 | + "in the Potassium release. Please use the 'redis_cache' cache backend instead.", |
246 | 269 | )
|
247 |
| - except salt.exceptions.SaltDeserializationError: |
248 |
| - log.warning("Failed to load token %r - removing broken/empty file.", tok) |
249 |
| - rm_tok = True |
250 |
| - else: |
251 |
| - if not tdata: |
| 270 | + |
| 271 | + tdata = {} |
| 272 | + try: |
| 273 | + tdata = self.tokens["{}.get_token".format(self.opts["eauth_tokens"])]( |
| 274 | + self.opts, tok |
| 275 | + ) |
| 276 | + except salt.exceptions.SaltDeserializationError: |
| 277 | + log.warning( |
| 278 | + "Failed to load token %r - removing broken/empty file.", tok |
| 279 | + ) |
| 280 | + rm_tok = True |
| 281 | + else: |
| 282 | + if not tdata: |
| 283 | + return {} |
| 284 | + rm_tok = False |
| 285 | + |
| 286 | + if tdata.get("expire", 0) < time.time(): |
| 287 | + # If expire isn't present in the token it's invalid and needs |
| 288 | + # to be removed. Also, if it's present and has expired - in |
| 289 | + # other words, the expiration is before right now, it should |
| 290 | + # be removed. |
| 291 | + rm_tok = True |
| 292 | + |
| 293 | + if rm_tok: |
| 294 | + self.rm_token(tok) |
252 | 295 | return {}
|
253 |
| - rm_tok = False |
254 | 296 |
|
255 |
| - if tdata.get("expire", 0) < time.time(): |
256 |
| - # If expire isn't present in the token it's invalid and needs |
257 |
| - # to be removed. Also, if it's present and has expired - in |
258 |
| - # other words, the expiration is before right now, it should |
259 |
| - # be removed. |
260 |
| - rm_tok = True |
| 297 | + return tdata |
| 298 | + else: |
| 299 | + try: |
| 300 | + tdata = self.cache.fetch("tokens", tok) |
261 | 301 |
|
262 |
| - if rm_tok: |
263 |
| - self.rm_token(tok) |
264 |
| - return {} |
| 302 | + if tdata.get("expire", 0) < time.time(): |
| 303 | + raise salt.exceptions.TokenExpiredError |
265 | 304 |
|
266 |
| - return tdata |
| 305 | + return tdata |
| 306 | + except ( |
| 307 | + salt.exceptions.SaltDeserializationError, |
| 308 | + salt.exceptions.TokenExpiredError, |
| 309 | + ): |
| 310 | + log.warning( |
| 311 | + "Failed to load token %r - removing broken/empty file.", tok |
| 312 | + ) |
| 313 | + self.rm_token(tok) |
| 314 | + except salt.exceptions.SaltCacheError as err: |
| 315 | + log.error( |
| 316 | + "Cannot get token %s from tokens cache using %s: %s", |
| 317 | + tok, |
| 318 | + self.opts["eauth_tokens.cache_driver"], |
| 319 | + err, |
| 320 | + ) |
| 321 | + return {} |
267 | 322 |
|
268 | 323 | def list_tokens(self):
|
269 | 324 | """
|
270 | 325 | List all tokens in eauth_tokens storage.
|
271 | 326 | """
|
272 |
| - return self.tokens["{}.list_tokens".format(self.opts["eauth_tokens"])]( |
273 |
| - self.opts |
274 |
| - ) |
| 327 | + if self.opts["eauth_tokens.cache_driver"] == "rediscluster": |
| 328 | + salt.utils.versions.warn_until( |
| 329 | + 3009, |
| 330 | + "The 'rediscluster' token backend has been deprecated, and will be removed " |
| 331 | + "in the Potassium release. Please use the 'redis_cache' cache backend instead.", |
| 332 | + ) |
| 333 | + |
| 334 | + return self.tokens["{}.list_tokens".format(self.opts["eauth_tokens"])]( |
| 335 | + self.opts |
| 336 | + ) |
| 337 | + else: |
| 338 | + try: |
| 339 | + return self.cache.list("tokens") |
| 340 | + except salt.exceptions.SaltCacheError as err: |
| 341 | + log.error( |
| 342 | + "Cannot list tokens from tokens cache using %s: %s", |
| 343 | + self.opts["eauth_tokens.cache_driver"], |
| 344 | + err, |
| 345 | + ) |
| 346 | + return [] |
275 | 347 |
|
276 | 348 | def rm_token(self, tok):
|
277 | 349 | """
|
278 | 350 | Remove the given token from token storage.
|
279 | 351 | """
|
280 |
| - self.tokens["{}.rm_token".format(self.opts["eauth_tokens"])](self.opts, tok) |
| 352 | + if self.opts["eauth_tokens.cache_driver"] == "rediscluster": |
| 353 | + salt.utils.versions.warn_until( |
| 354 | + 3009, |
| 355 | + "The 'rediscluster' token backend has been deprecated, and will be removed " |
| 356 | + "in the Potassium release. Please use the 'redis_cache' cache backend instead.", |
| 357 | + ) |
| 358 | + |
| 359 | + self.tokens["{}.rm_token".format(self.opts["eauth_tokens"])](self.opts, tok) |
| 360 | + else: |
| 361 | + try: |
| 362 | + return self.cache.flush("tokens", tok) |
| 363 | + except salt.exceptions.SaltCacheError as err: |
| 364 | + log.error( |
| 365 | + "Cannot rm token %s from tokens cache using %s: %s", |
| 366 | + tok, |
| 367 | + self.opts["eauth_tokens.cache_driver"], |
| 368 | + err, |
| 369 | + ) |
| 370 | + return {} |
| 371 | + |
| 372 | + def clean_expired_tokens(self): |
| 373 | + """ |
| 374 | + Clean expired tokens |
| 375 | + """ |
| 376 | + if self.opts["eauth_tokens.cache_driver"] == "rediscluster": |
| 377 | + salt.utils.versions.warn_until( |
| 378 | + 3009, |
| 379 | + "The 'rediscluster' token backend has been deprecated, and will be removed " |
| 380 | + "in the Potassium release. Please use the 'redis_cache' cache backend instead.", |
| 381 | + ) |
| 382 | + log.debug( |
| 383 | + "cleaning expired tokens using token driver: {}".format( |
| 384 | + self.opts["eauth_tokens"] |
| 385 | + ) |
| 386 | + ) |
| 387 | + for token in self.list_tokens(): |
| 388 | + token_data = self.get_tok(token) |
| 389 | + if ( |
| 390 | + "expire" not in token_data |
| 391 | + or token_data.get("expire", 0) < time.time() |
| 392 | + ): |
| 393 | + self.rm_token(token) |
| 394 | + else: |
| 395 | + self.cache.clean_expired("tokens") |
281 | 396 |
|
282 | 397 | def authenticate_token(self, load):
|
283 | 398 | """
|
@@ -604,6 +719,15 @@ def get_token(self, token):
|
604 | 719 | tdata = self._send_token_request(load)
|
605 | 720 | return tdata
|
606 | 721 |
|
| 722 | + def rm_token(self, token): |
| 723 | + """ |
| 724 | + Delete a token from the master |
| 725 | + """ |
| 726 | + load = {} |
| 727 | + load["token"] = token |
| 728 | + load["cmd"] = "rm_token" |
| 729 | + self._send_token_request(load) |
| 730 | + |
607 | 731 |
|
608 | 732 | class AuthUser:
|
609 | 733 | """
|
|
0 commit comments