@@ -694,6 +694,88 @@ def upload_data_and_wait(self, file_path: str, poll_interval: int = 5, timeout_s
694694 print (f"Error in upload and wait: { e } " )
695695 return False
696696
697+ def verify_token (self ) -> bool :
698+ """Verify if the current token is valid by making a test request"""
699+ try :
700+ # Try to make a simple API call to verify the token
701+ response = self .session .get (
702+ f"{ self .base_url } /api/v2/file-upload" ,
703+ headers = self ._get_headers ()
704+ )
705+ return response .status_code == 200
706+ except Exception :
707+ return False
708+
709+ def auto_renew_token (self ) -> bool :
710+ """Automatically renew the token using stored credentials"""
711+ try :
712+ # Load config to get stored credentials
713+ config = configparser .ConfigParser ()
714+ config .read (os .path .expanduser ("~/.bloodhound_config" ))
715+
716+ if 'CE' not in config :
717+ return False
718+
719+ username = config ['CE' ].get ('username' , 'admin' )
720+ password = config ['CE' ].get ('password' )
721+ base_url = config ['CE' ].get ('base_url' , 'http://localhost:8080' )
722+
723+ if not password :
724+ return False
725+
726+ # Create a new session for authentication (without the expired token)
727+ import requests
728+ temp_session = requests .Session ()
729+ temp_session .verify = self .session .verify
730+
731+ # Authenticate with stored credentials using the temp session
732+ login_url = f"{ base_url } /api/v2/login"
733+ payload = {"login_method" : "secret" , "username" : username , "secret" : password }
734+
735+ response = temp_session .post (login_url , json = payload , timeout = 60 )
736+ if response .status_code >= 400 :
737+ return False
738+
739+ data = response .json ()
740+ token = None
741+ if isinstance (data , dict ):
742+ data_field = data .get ("data" )
743+ if isinstance (data_field , dict ):
744+ token = data_field .get ("session_token" )
745+ if not token :
746+ token = data .get ("token" ) or data .get ("access_token" ) or data .get ("jwt" )
747+
748+ if not token :
749+ return False
750+
751+ # Update the stored token and our session
752+ config ['CE' ]['api_token' ] = token
753+ with open (os .path .expanduser ("~/.bloodhound_config" ), 'w' ) as f :
754+ config .write (f )
755+
756+ # Update our session with the new token
757+ self .api_token = token
758+ self .session .headers .update ({"Authorization" : f"Bearer { token } " })
759+
760+ return True
761+
762+ except Exception as e :
763+ print (f"Error auto-renewing token: { e } " )
764+ return False
765+
766+ def ensure_valid_token (self ) -> bool :
767+ """Ensure we have a valid token, auto-renew if necessary"""
768+ if not self .api_token :
769+ return self .auto_renew_token ()
770+
771+ # Check if current token is valid
772+ if self .verify_token ():
773+ return True
774+
775+ # Token is invalid, try to renew
776+ print ("Token expired, attempting to renew..." )
777+ return self .auto_renew_token ()
778+
697779 def close (self ):
698780 """Close the HTTP session"""
699781 try :
0 commit comments