dcicutils 6.9.9.0b4__py3-none-any.whl → 6.9.9.0b6__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of dcicutils might be problematic. Click here for more details.

dcicutils/ff_mocks.py CHANGED
@@ -190,6 +190,23 @@ def mocked_s3utils(environments=None, require_sse=False, other_access_key_names=
190
190
  yield mock_boto3
191
191
 
192
192
 
193
+ DEFAULT_SSE_ENVIRONMENTS_TO_MOCK = ['fourfront-foo', 'fourfront-bar']
194
+
195
+
196
+ @contextlib.contextmanager
197
+ def mocked_s3utils_with_sse(beanstalks=None, environments=None, require_sse=True, files=None):
198
+ # beanstalks= is deprecated. Use environments= instead.
199
+ environments = (DEFAULT_SSE_ENVIRONMENTS_TO_MOCK
200
+ if beanstalks is None and environments is None
201
+ else (beanstalks or []) + (environments or []))
202
+ with mocked_s3utils(environments=environments, require_sse=require_sse) as mock_boto3:
203
+ s3 = mock_boto3.client('s3')
204
+ assert isinstance(s3, MockBotoS3Client)
205
+ for filename, string in (files or {}).items():
206
+ s3.s3_files.files[filename] = string.encode('utf-8')
207
+ yield mock_boto3
208
+
209
+
193
210
  # Here we set up some variables, auxiliary functions, and mocks containing common values needed for testing
194
211
  # of the next several functions so that the functions don't have to set them up over
195
212
  # and over again in each test.
dcicutils/ff_utils.py CHANGED
@@ -6,13 +6,11 @@ import requests
6
6
  import time
7
7
 
8
8
  from collections import namedtuple
9
+ from dcicutils.lang_utils import disjoined_list
9
10
  from elasticsearch.exceptions import AuthorizationException
10
11
  from typing import Dict, List
11
12
  from urllib.parse import parse_qs, urlencode, urlparse, urlunparse
12
- from . import (
13
- s3_utils,
14
- es_utils,
15
- )
13
+ from . import s3_utils, es_utils
16
14
  from .misc_utils import PRINT
17
15
 
18
16
 
@@ -107,8 +105,6 @@ def search_request_with_retries(request_fxn, url, auth, verb, **kwargs):
107
105
  try:
108
106
  res_json = res.json()
109
107
  except ValueError:
110
- # PyCharm notes this is unused. -kmp 17-Jul-2020
111
- # res_json = {}
112
108
  try:
113
109
  res.raise_for_status()
114
110
  except Exception as e:
@@ -238,7 +234,7 @@ def internal_compute_authorized_request(url, *, auth, ff_env, verb, retry_fxn, *
238
234
  authorized_request('https://data.4dnucleome.org/<some path>', ff_env='fourfront-webprod')
239
235
  """
240
236
  # Save to uncomment if debugging unit tests...
241
- # print("authorized_request\n URL=%s\n auth=%s\n ff_env=%s\n verb=%s\n" % (url, auth, ff_env, verb))
237
+ # print(f"authorized_request\n URL={url}\n auth={auth}\n ff_env={ff_env}\n verb={verb}\n")
242
238
  use_auth = unified_authentication(auth, ff_env)
243
239
  headers = kwargs.get('headers')
244
240
  if not headers:
@@ -246,11 +242,12 @@ def internal_compute_authorized_request(url, *, auth, ff_env, verb, retry_fxn, *
246
242
  if 'timeout' not in kwargs:
247
243
  kwargs['timeout'] = 60 # default timeout
248
244
 
245
+ verb_upper = verb
249
246
  try:
250
- the_verb = REQUESTS_VERBS[verb.upper()]
247
+ verb_upper = verb.upper()
248
+ the_verb = REQUESTS_VERBS[verb_upper]
251
249
  except KeyError:
252
- raise ValueError("Provided verb %s is not valid. Must be one of: %s"
253
- % (verb.upper(), ', '.join(REQUESTS_VERBS.keys())))
250
+ raise ValueError(f"Provided verb {verb} is not valid. Must be one of {disjoined_list(REQUESTS_VERBS)}.")
254
251
  # automatically detect a search and overwrite the retry if it is standard
255
252
  if '/search/' in url and retry_fxn == standard_request_with_retries:
256
253
  retry_fxn = search_request_with_retries
@@ -262,7 +259,15 @@ def internal_compute_authorized_request(url, *, auth, ff_env, verb, retry_fxn, *
262
259
 
263
260
 
264
261
  def _sls(val):
265
- """general helper to check for and strip leading slashes on ids in API fxns
262
+ """
263
+ Helper to strip any leading slashes on ids in API functions.
264
+ Examples:
265
+ >>> _sls('foo')
266
+ foo
267
+ >>> _sls('/foo')
268
+ foo
269
+ >>> _sls('/foo/bar/baz/')
270
+ foo/bar/baz/
266
271
  """
267
272
  return val.lstrip('/')
268
273
 
dcicutils/misc_utils.py CHANGED
@@ -12,6 +12,7 @@ import io
12
12
  import os
13
13
  import logging
14
14
  import pytz
15
+ import re
15
16
  import rfc3986.validators
16
17
  import rfc3986.exceptions
17
18
  import time
@@ -1316,6 +1317,17 @@ def string_list(s):
1316
1317
  return [p for p in [part.strip() for part in s.split(",")] if p]
1317
1318
 
1318
1319
 
1320
+ def is_c4_arn(arn: str) -> bool:
1321
+ """
1322
+ Returns True iff the given (presumed) AWS ARN string value looks like it
1323
+ refers to a CGAP or Fourfront Cloudformation entity, otherwise returns False.
1324
+ :param arn: String representing an AWS ARN.
1325
+ :return: True if the given ARN refers to a CGAP or Fourfront Cloudformation entity, else False.
1326
+ """
1327
+ pattern = r"(fourfront|cgap|[:/]c4-)"
1328
+ return True if re.search(pattern, arn) else False
1329
+
1330
+
1319
1331
  def string_md5(unicode_string):
1320
1332
  """
1321
1333
  Returns the md5 signature for the given u unicode string.
dcicutils/redis_tools.py CHANGED
@@ -24,29 +24,16 @@ class RedisSessionToken:
24
24
  Model used by Redis to store session tokens
25
25
  Keystore structure:
26
26
  <env_namespace>:session:<token>
27
- -> Redis hset containing the associated JWT and expiration time (3 hours)
27
+ -> Redis str of JWT with automated expiration
28
28
  """
29
- JWT = 'jwt'
30
- EXPIRATION = 'expiration'
31
29
 
32
30
  @staticmethod
33
- def _build_session_expiration(n_hours: int = 3) -> str:
31
+ def _build_session_expiration(n_hours: int = 3) -> datetime.timedelta:
34
32
  """ Builds a session expiration date 3 hours after generation
35
33
  :param n_hours: timestamp after which the session token is invalid
36
34
  :return: datetime string 3 hours from creation time
37
35
  """
38
- return str(datetime.datetime.utcnow() + datetime.timedelta(hours=n_hours))
39
-
40
- def _build_session_hset(self, jwt: str, expiration=None) -> dict:
41
- """ Builds Redis hset record for the session token
42
- :param jwt: encoded jwt value
43
- :param expiration: expiration if using an existing one
44
- :return: dictionary to be stored as a hash in Redis
45
- """
46
- return {
47
- self.JWT: jwt,
48
- self.EXPIRATION: self._build_session_expiration() if not expiration else expiration
49
- }
36
+ return datetime.timedelta(hours=n_hours)
50
37
 
51
38
  @staticmethod
52
39
  def _build_redis_key(namespace: str, token: str) -> str:
@@ -72,23 +59,23 @@ class RedisSessionToken:
72
59
  self.namespace = namespace
73
60
  self.redis_key = self._build_redis_key(self.namespace, self.session_token)
74
61
  self.jwt = jwt
75
- self.session_hset = self._build_session_hset(self.jwt, expiration=expiration)
62
+ self.expiration = expiration or self._build_session_expiration()
76
63
 
77
64
  def __eq__(self, other):
78
65
  """ Evaluates equality of two session objects based on the value of the session hset """
79
- return self.session_hset == other.session_hset
66
+ return (self.redis_key == other.redis_key) and (self.jwt == other.jwt)
80
67
 
81
68
  def get_session_token(self) -> str:
82
69
  """ Extracts the session token stored on this object """
83
70
  return self.session_token
84
71
 
85
72
  def get_redis_key(self) -> str:
86
- """ Returns the key under which the Redis hset is stored - note that this contains the token! """
73
+ """ Returns the key under which the Redis set is stored - note that this contains the token! """
87
74
  return self.redis_key
88
75
 
89
- def get_expiration(self) -> str:
90
- """ Returns the expiration date stored in the session hset locally """
91
- return self.session_hset[self.EXPIRATION]
76
+ def get_expiration(self) -> datetime:
77
+ """ Returns the expiration timedelta """
78
+ return self.expiration
92
79
 
93
80
  @classmethod
94
81
  def from_redis(cls, *, redis_handler: RedisBase, namespace: str, token: str):
@@ -100,10 +87,11 @@ class RedisSessionToken:
100
87
  :return: A RedisSessionToken object built from an existing record in Redis
101
88
  """
102
89
  redis_key = f'{namespace}:session:{token}'
103
- redis_entry = redis_handler.hgetall(redis_key)
90
+ redis_entry = redis_handler.get(redis_key)
104
91
  if redis_entry:
105
- return cls(namespace=namespace, jwt=redis_entry[cls.JWT],
106
- token=token, expiration=redis_entry[cls.EXPIRATION])
92
+ expiration = redis_handler.ttl(redis_key)
93
+ return cls(namespace=namespace, jwt=redis_entry,
94
+ token=token, expiration=expiration)
107
95
 
108
96
  def decode_jwt(self, audience: str, secret: str, leeway: int = 30) -> dict:
109
97
  """ Decodes JWT to grab info such as the email
@@ -121,7 +109,7 @@ class RedisSessionToken:
121
109
  :return: True if successful, raise Exception otherwise
122
110
  """
123
111
  try:
124
- redis_handler.hset_multiple(self.redis_key, self.session_hset)
112
+ redis_handler.set(self.redis_key, self.jwt, exp=self.expiration)
125
113
  except Exception as e:
126
114
  log.error(str(e))
127
115
  raise RedisException()
@@ -132,15 +120,12 @@ class RedisSessionToken:
132
120
  :param redis_handler: handle to Redis API
133
121
  :return: True if token matches that in Redis and is not expired
134
122
  """
135
- redis_token = redis_handler.hgetall(self.redis_key)
123
+ redis_token = redis_handler.get(self.redis_key)
136
124
  if not redis_token:
137
125
  return False # if it doesn't exist it's not valid
138
- timestamp_is_valid = (datetime.datetime.fromisoformat(
139
- redis_token[self.EXPIRATION]) > datetime.datetime.utcnow()
140
- )
141
- return timestamp_is_valid
126
+ return True # if it does exist it must be valid since we always send with TTL
142
127
 
143
- def update_session_token(self, *, redis_handler: RedisBase, jwt) -> bool:
128
+ def update_session_token(self, *, redis_handler: RedisBase, jwt: str) -> bool:
144
129
  """ Refreshes the session token, jwt (if different) and expiration stored in Redis
145
130
  :param redis_handler: handle to Redis API
146
131
  :param jwt: jwt of user
@@ -151,13 +136,13 @@ class RedisSessionToken:
151
136
  # build new one
152
137
  self.session_token = make_session_token()
153
138
  self.redis_key = self._build_redis_key(self.namespace, self.session_token)
139
+ self.expiration = self._build_session_expiration()
154
140
  self.jwt = jwt
155
- self.session_hset = self._build_session_hset(jwt)
156
141
  return self.store_session_token(redis_handler=redis_handler)
157
142
 
158
- def delete_session_token(self, *, redis_handler) -> bool:
143
+ def delete_session_token(self, *, redis_handler: RedisBase) -> bool:
159
144
  """ Deletes the session token from redis, effectively logging out
160
145
  :param redis_handler: handle to Redis API
161
146
  :return: True if successful, False otherwise
162
147
  """
163
- return redis_handler.delete(self.redis_key)
148
+ return 1 == redis_handler.delete(self.redis_key)
dcicutils/redis_utils.py CHANGED
@@ -1,4 +1,5 @@
1
1
  import redis
2
+ import datetime
2
3
  from typing import Union
3
4
  # Low level utilities for working with Redis
4
5
 
@@ -59,13 +60,17 @@ class RedisBase(object):
59
60
  """
60
61
  return self.redis.info()
61
62
 
62
- def set(self, key: str, value: Union[str, int, float]) -> str:
63
+ def set(self, key: str, value: Union[str, int, float], exp: Union[int, datetime.timedelta] = None) -> str:
63
64
  """ Sets the given key to the given value https://redis.io/commands/set/
64
65
  :param key: string to store value under
65
66
  :param value: value mapped to from key
67
+ :param exp: expiration time in seconds as int or datetime.timedelta (optional)
66
68
  :return: a "true" string <OK>
67
69
  """
68
- return self.redis.set(self._encode_value(key), self._encode_value(value))
70
+ kwargs = {}
71
+ if exp:
72
+ kwargs['ex'] = exp
73
+ return self.redis.set(self._encode_value(key), self._encode_value(value), **kwargs)
69
74
 
70
75
  def get(self, key: str) -> str:
71
76
  """ Gets the given key from Redis https://redis.io/commands/get/
@@ -77,6 +82,21 @@ class RedisBase(object):
77
82
  val = self._decode_value(val)
78
83
  return val
79
84
 
85
+ def set_expiration(self, key: str, t: Union[int, datetime.time]) -> bool:
86
+ """ Sets the TTL of the given key manually
87
+ :param key: key to set TTL
88
+ :param t: time in seconds until expiration (datetime.timedelta or int)
89
+ :returns: True if successful
90
+ """
91
+ return self.redis.expire(key, t, gt=True)
92
+
93
+ def ttl(self, key: str) -> datetime.time:
94
+ """ Gets the TTL of the given key
95
+ :param key: key to get TTL for
96
+ :return: datetime value in seconds
97
+ """
98
+ return self.redis.ttl(key)
99
+
80
100
  def delete(self, key: str) -> int:
81
101
  """ Deletes the given key from Redis https://redis.io/commands/del/
82
102
  :param key: key to delete
@@ -117,7 +117,8 @@ def apply_overrides(*, secrets: dict, rename_keys: Optional[dict] = None,
117
117
  raise ValueError(f"Cannot rename {key} to {new_name}"
118
118
  f" because {a_or_an(new_name)} attribute already exists.")
119
119
  if key not in secrets:
120
- raise ValueError(f"Cannot rename {key} to {new_name} because the secrets has no {key} attribute.")
120
+ logger.warning(f"Cannot rename {key} to {new_name} because the secrets has no {key} attribute.")
121
+ continue
121
122
  secrets[new_name] = secrets.pop(key)
122
123
  if override_values:
123
124
  secrets = dict(secrets, **override_values)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dcicutils
3
- Version: 6.9.9.0b4
3
+ Version: 6.9.9.0b6
4
4
  Summary: Utility package for interacting with the 4DN Data Portal and other 4DN resources
5
5
  Home-page: https://github.com/4dn-dcic/utils
6
6
  License: MIT
@@ -20,23 +20,23 @@ dcicutils/env_utils.py,sha256=MQ3zcXcbxJV9dO5y1nN2HNswtiUw2VDK4KrROrZOUtA,46285
20
20
  dcicutils/env_utils_legacy.py,sha256=J81OAtJHN69o1beHO6q1j7_J6TeblSjnAHlS8VA5KSM,29032
21
21
  dcicutils/es_utils.py,sha256=ZksLh5ei7kRUfiFltk8sd2ZSfh15twbstrMzBr8HNw4,7541
22
22
  dcicutils/exceptions.py,sha256=fO-zIwcT0kdy-BL1qczva3QLY9ZEkhAFVGfBS9w-Row,9257
23
- dcicutils/ff_mocks.py,sha256=eUdIJQNaNf5UCT7_-ruH3HXP5rlKQNHW3OMKpHymlGQ,36260
24
- dcicutils/ff_utils.py,sha256=I25EmbKzPm-V7zEaMbx6fZPSD3OKh5sLmKrnsopHtus,64240
23
+ dcicutils/ff_mocks.py,sha256=wmf7-_C0cq9bpZZGUyKQO6eC-1p2ltKPVCSi-ln5y68,37025
24
+ dcicutils/ff_utils.py,sha256=nHrVyf252HgP1mnerFmqJzUo6_Cm2hRk-Rnj5KN1RUQ,64324
25
25
  dcicutils/jh_utils.py,sha256=Gpsxb9XEzggF_-Eq3ukjKvTnuyb9V1SCSUXkXsES4Kg,11502
26
26
  dcicutils/kibana/dashboards.json,sha256=wHMB_mpJ8OaYhRRgvpZuihaB2lmSF64ADt_8hkBWgQg,16225
27
27
  dcicutils/kibana/readme.md,sha256=3KmHF9FH6A6xwYsNxRFLw27q0XzHYnjZOlYUnn3VkQQ,2164
28
28
  dcicutils/lang_utils.py,sha256=MaCJG-4KB2oOd3wcdd0e048eRBLkoxCKFkqR1KlrjkI,27302
29
29
  dcicutils/log_utils.py,sha256=7pWMc6vyrorUZQf-V-M3YC6zrPgNhuV_fzm9xqTPph0,10883
30
- dcicutils/misc_utils.py,sha256=iz-MAy4OlmQJUY2yD5dMRqfcEtNhfBHVkLdCi8QXdsg,86187
30
+ dcicutils/misc_utils.py,sha256=jRazVlj2gJ_DZsI2JjPIEfTmFLkcxDwPQStqGtaGMRc,86651
31
31
  dcicutils/obfuscation_utils.py,sha256=xUCQYAe-nQY3vCKMLjT_3tAFhIeYkJuhMaZnxunF0Hc,5510
32
32
  dcicutils/opensearch_utils.py,sha256=V2exmFYW8Xl2_pGFixF4I2Cc549Opwe4PhFi5twC0M8,1017
33
33
  dcicutils/qa_checkers.py,sha256=hdduo-6ZITEurrmZYjll5N-nKQeyKaFLbERE9d4gRdU,20232
34
34
  dcicutils/qa_utils.py,sha256=g0nuAigZUWCXd4ZBqOKHxLq1H3AGI2X2wD57rvBdAOY,120561
35
- dcicutils/redis_tools.py,sha256=7RzP5zqL2DCFivvrDsaq-PHqNdfpGpuWtCq6VU0n3uU,7016
36
- dcicutils/redis_utils.py,sha256=vV-7NwRXVrQZhJMFnU-0qAZTfZX9tWEvJjDojogsmd0,5677
35
+ dcicutils/redis_tools.py,sha256=VQ5cTnDnKQ8AX0n9iuikCqtEUtIlE0ZOEqVZR3iNGTI,6389
36
+ dcicutils/redis_utils.py,sha256=VJ-7g8pOZqR1ZCtdcjKz3-6as2DMUcs1b1zG6wSprH4,6462
37
37
  dcicutils/s3_utils.py,sha256=PAIUXpv7l7NNK0IzVLS6URJhLxg-SxLbZc0fJTpjI8E,25659
38
- dcicutils/secrets_utils.py,sha256=d-oVHmyJL6Bcslxth73Ab31GeNoYtygWoVtOuzZ0xVQ,19722
38
+ dcicutils/secrets_utils.py,sha256=8dppXAsiHhJzI6NmOcvJV5ldvKkQZzh3Fl-cb8Wm7MI,19745
39
39
  dcicutils/snapshot_utils.py,sha256=ymP7PXH6-yEiXAt75w0ldQFciGNqWBClNxC5gfX2FnY,22961
40
- dcicutils-6.9.9.0b4.dist-info/WHEEL,sha256=DA86_h4QwwzGeRoz62o1svYt5kGEXpoUTuTtwzoTb30,83
41
- dcicutils-6.9.9.0b4.dist-info/METADATA,sha256=FkIXTNsXnmoj8J0KZ4vq2A8tXlIcMd79ls7pgFBz3_8,2722
42
- dcicutils-6.9.9.0b4.dist-info/RECORD,,
40
+ dcicutils-6.9.9.0b6.dist-info/WHEEL,sha256=DA86_h4QwwzGeRoz62o1svYt5kGEXpoUTuTtwzoTb30,83
41
+ dcicutils-6.9.9.0b6.dist-info/METADATA,sha256=oWx6nepGIzhI4H0gi13-CV8KXVaYXtaSRmi8R4F1GUM,2722
42
+ dcicutils-6.9.9.0b6.dist-info/RECORD,,