1- """Integrated caching strategy for staff users."""
1+ """
2+ CachedStaffUserService provides methods for staff user data retrieval.
23
3- import hashlib
4- from flask import current_app , g
4+ NOTE:
5+ Staff caching has been intentionally removed for in production.
6+ This service always fetches fresh data to avoid cross-pod
7+ permission inconsistencies.
8+ Re-enable only with a shared cache, not a Simple cache.
9+ """
10+
11+ from flask import current_app
512
613from compliance_api .models .staff_user import StaffUser as StaffUserModel
714from compliance_api .schemas .staff_user import StaffUserSchema
815from compliance_api .services .authorize_service .auth_service import AuthService
9- from compliance_api .utils .cache import cache
1016
1117
1218class CachedStaffUserService :
1319 """Cached staff user service for performance optimization during token validation."""
1420
15- CACHE_TIMEOUT = 3600 # 1 hour
16- STAFF_CACHE_KEY_PREFIX = "staff_user:"
17- ALL_STAFF_CACHE_KEY = "all_staff_users"
18- ALL_STAFF_WITH_AUTH_PREFIX = "all_staff_with_auth:"
19- CACHE_VERSION_KEY = "staff_cache_version"
20-
21- @classmethod
22- def _get_cache_version (cls ):
23- """
24- Get current cache version from shared cache.
25-
26- This ensures all pods see the same version number.
27- """
28- version = cache .get (cls .CACHE_VERSION_KEY )
29- if version is None :
30- version = 0
31- cache .set (cls .CACHE_VERSION_KEY , version )
32- return version
33-
3421 @classmethod
3522 def exists_staff_by_auth_guid (cls , auth_guid : str ) -> bool :
3623 """
@@ -48,130 +35,36 @@ def exists_staff_by_auth_guid(cls, auth_guid: str) -> bool:
4835 if not auth_guid :
4936 return False
5037
51- # Include version in cache key
52- version = cls ._get_cache_version ()
53- cache_key = f"{ cls .STAFF_CACHE_KEY_PREFIX } { auth_guid } :v{ version } "
54-
55- cached_staff_exists = cache .get (cache_key )
56- if cached_staff_exists is not None :
57- current_app .logger .debug (
58- f"Cache hit for staff existence check: { auth_guid } "
59- )
60- return bool (cached_staff_exists )
38+ current_app .logger .debug (
39+ f"Fetching staff existence from DB for auth_guid={ auth_guid } "
40+ )
6141
62- current_app .logger .debug (f"Cache miss for staff existence check: { auth_guid } " )
6342 staff_user = StaffUserModel .get_by_auth_guid (auth_guid )
64-
65- cache_value = bool (staff_user )
66- cache .set (cache_key , cache_value , timeout = cls .CACHE_TIMEOUT )
67-
68- return cache_value
43+ return bool (staff_user )
6944
7045 @staticmethod
7146 def get_all_staff_users_with_auth ():
7247 """
73- Get all staff users with merged auth data (cached as serialized data) .
48+ Get all staff users with merged auth data.
7449
7550 Returns:
7651 List of serialized staff user dictionaries
7752 """
7853 # pylint: disable=import-outside-toplevel
7954 from compliance_api .services .staff_user import _set_permission_level_in_compliance_user_obj
8055
81- # Get current version from shared cache
82- version = CachedStaffUserService ._get_cache_version ()
83-
84- # Create cache key that includes token hash AND version
85- token_hash = CachedStaffUserService ._get_token_hash ()
86- cache_key = (
87- f"{ CachedStaffUserService .ALL_STAFF_WITH_AUTH_PREFIX } "
88- f"{ token_hash } :v{ version } "
56+ current_app .logger .debug (
57+ "Fetching all staff users with auth data (no cache)"
8958 )
9059
91- cached_result = cache .get (cache_key )
92- if cached_result is not None :
93- current_app .logger .debug ("Cache hit for all staff users with auth data" )
94- return cached_result
95-
96- current_app .logger .debug ("Cache miss for all staff users with auth data" )
97-
98- # Get users from compliance database with eager loading
9960 users = StaffUserModel .get_all_with_relationships (default_filters = False )
10061
101- # Get compliance users from epic system
10262 auth_users = AuthService .get_epic_users_by_app ()
103-
104- # Merge the two sets of users to set the permission in the result
10563 index_auth_users = {user ["username" ]: user for user in auth_users }
64+
10665 for user in users :
107- auth_user = index_auth_users .get (user .auth_user_guid , None )
108- user = _set_permission_level_in_compliance_user_obj (user , auth_user )
66+ auth_user = index_auth_users .get (user .auth_user_guid )
67+ _set_permission_level_in_compliance_user_obj (user , auth_user )
10968
110- # Serialize the data BEFORE caching to avoid detached instance issues
11169 user_schema = StaffUserSchema (many = True )
112- serialized_users = user_schema .dump (users )
113-
114- # Cache the serialized data
115- cache .set (cache_key , serialized_users , timeout = 300 ) # 5 minutes
116-
117- return serialized_users
118-
119- @staticmethod
120- def invalidate_staff_cache (auth_guid : str = None ):
121- """
122- Invalidate cached staff user data.
123-
124- Args:
125- auth_guid: Specific auth_guid to invalidate, or None to clear all staff cache
126- """
127- # Bump version to invalidate all caches across all pods
128- CachedStaffUserService ._invalidate_all_staff_cache ()
129-
130- if auth_guid :
131- current_app .logger .info (f"Invalidated cache for staff user: { auth_guid } " )
132-
133- @staticmethod
134- def _invalidate_all_staff_cache ():
135- """
136- Invalidate all staff cache by bumping version number.
137-
138- This invalidates cache for across pods since the version
139- is part of every cache key.
140- """
141- try :
142- current_version = cache .get (CachedStaffUserService .CACHE_VERSION_KEY )
143- if current_version is None :
144- current_version = 0
145-
146- new_version = current_version + 1
147- cache .set (CachedStaffUserService .CACHE_VERSION_KEY , new_version )
148- current_app .logger .info (
149- f"Bumped staff cache version from { current_version } to { new_version } "
150- )
151- except (AttributeError , RuntimeError ) as e :
152- current_app .logger .error (f"Error invalidating staff cache: { e } " )
153-
154- @classmethod
155- def _clear_all_staff_cache (cls ):
156- """Clear all staff-related cache entries."""
157- try :
158- cache .clear ()
159- current_app .logger .info ("Cleared all cache (simple cache type)" )
160- except (AttributeError , RuntimeError ) as e :
161- current_app .logger .error (f"Error clearing cache: { str (e )} " )
162-
163- @staticmethod
164- def _get_token_hash ():
165- """
166- Generate a cache key suffix based on the current user's token.
167-
168- This ensures different users get different cached data while
169- not exposing the actual token in cache keys.
170- """
171- token = getattr (g , "access_token" , None )
172- if not token :
173- return "no_token"
174-
175- # Use first 16 chars of SHA256 hash
176- token_hash = hashlib .sha256 (token .encode ()).hexdigest ()[:16 ]
177- return token_hash
70+ return user_schema .dump (users )
0 commit comments