33import getpass
44from enum import Enum
55from pathlib import Path
6- from typing import TYPE_CHECKING , List , Optional , Union
7- from warnings import warn
6+ from typing import List , Optional , Union
87
98import yaml
9+ from oold .backend .auth import UserPwdCredential as _OoldUserPwdCredential
10+ from oold .backend .auth import find_credential as _find_credential
11+ from oold .backend .auth import load_credentials as _load_credentials
1012from opensemantic .v1 import OswBaseModel
1113from pydantic .v1 import PrivateAttr
1214
1315from osw .defaults import paths as default_paths
1416
15- if TYPE_CHECKING :
16- PossibleFilePath = Path
17- else :
18- from osw .custom_types import PossibleFilePath
17+
18+ def _secret_to_str (v ):
19+ """Unwrap SecretStr to plain str, pass through otherwise."""
20+ if hasattr (v , "get_secret_value" ):
21+ return v .get_secret_value ()
22+ return v
1923
2024
2125class CredentialManager (OswBaseModel ):
22- """Handles credentials"""
26+ """Handles credentials.
27+
28+ Delegates YAML loading and IRI matching to oold.backend.auth,
29+ adding osw-specific features (default paths, .gitignore management).
30+ Remains a v1 model because WtSiteConfig (v1) uses it as a field.
31+ """
2332
24- cred_filepath : Optional [
25- Union [Union [str , PossibleFilePath ], List [Union [str , PossibleFilePath ]]]
26- ]
33+ cred_filepath : Optional [Union [Union [str , Path ], List [Union [str , Path ]]]] = None
2734 """Filepath to yaml file with credentials for osw and connected services"""
28- cert_filepath : Optional [
29- Union [Union [str , PossibleFilePath ], List [Union [str , PossibleFilePath ]]]
30- ]
35+ cert_filepath : Optional [Union [Union [str , Path ], List [Union [str , Path ]]]] = None
3136 """Filepath to the certificates for osw and connected services"""
3237
33- _credentials : List [BaseCredential ] = PrivateAttr ([])
38+ _credentials : List [CredentialManager . BaseCredential ] = PrivateAttr ([])
3439 """in memory credential store"""
3540
3641 class BaseCredential (OswBaseModel ):
@@ -90,15 +95,47 @@ def __init__(self, **data):
9095 if not isinstance (self .cred_filepath , list ):
9196 self .cred_filepath = [self .cred_filepath ]
9297 self .cred_filepath = [Path (fp ) for fp in self .cred_filepath if fp != "" ]
93- # Make sure to at least warn the user if they pass cred_filepath instead of
94- # cred_filepath
95- attribute_names = self .__dict__ .keys ()
96- unexpected_kwargs = [key for key in data .keys () if key not in attribute_names ]
97- if unexpected_kwargs :
98- warn (f"Unexpected keyword argument(s): { ', ' .join (unexpected_kwargs )} " )
98+
99+ @staticmethod
100+ def _oold_to_osw (oold_cred ) -> CredentialManager .BaseCredential :
101+ """Convert an oold BaseCredential to an osw credential (plain str passwords)."""
102+ from oold .backend .auth import OAuth1Credential as _OoldOAuth1
103+
104+ if isinstance (oold_cred , _OoldOAuth1 ):
105+ return CredentialManager .OAuth1Credential (
106+ iri = oold_cred .iri ,
107+ consumer_token = oold_cred .consumer_token ,
108+ consumer_secret = _secret_to_str (oold_cred .consumer_secret ),
109+ access_token = oold_cred .access_token ,
110+ access_secret = _secret_to_str (oold_cred .access_secret ),
111+ )
112+ if isinstance (oold_cred , _OoldUserPwdCredential ):
113+ return CredentialManager .UserPwdCredential (
114+ iri = oold_cred .iri ,
115+ username = oold_cred .username ,
116+ password = _secret_to_str (oold_cred .password ),
117+ )
118+ return CredentialManager .BaseCredential (iri = oold_cred .iri )
119+
120+ def _load_file_credentials (self ):
121+ """Load credentials from YAML files using oold, return as dict."""
122+ all_creds = {}
123+ if self .cred_filepath :
124+ for fp in self .cred_filepath :
125+ fp = Path (fp )
126+ if not fp .exists ():
127+ continue
128+ try :
129+ loaded = _load_credentials (fp , into_store = False )
130+ all_creds .update (loaded )
131+ except Exception as exc :
132+ print (exc )
133+ return all_creds
99134
100135 def get_credential (self , config : CredentialConfig ) -> BaseCredential :
101- """Reads credentials from a yaml file or the in memory store
136+ """Reads credentials from a yaml file or the in memory store.
137+
138+ Uses oold.backend.auth.find_credential for IRI matching.
102139
103140 Parameters
104141 ----------
@@ -111,78 +148,36 @@ def get_credential(self, config: CredentialConfig) -> BaseCredential:
111148 Credential, contain attributes 'username' and 'password' and
112149 the matching iri.
113150 """
151+ oold_creds = self ._load_file_credentials ()
114152
115- _file_credentials : List [CredentialManager .BaseCredential ] = []
116- if self .cred_filepath :
117- filepaths = self .cred_filepath
118- if type (filepaths ) is not list :
119- filepaths = [filepaths ]
153+ for osw_cred in self ._credentials :
154+ oold_creds [osw_cred .iri ] = osw_cred
120155
121- for filepath in filepaths :
122- if not filepath .exists ():
123- continue
124- with open (filepath , "r" , encoding = "utf-8" ) as stream :
125- try :
126- accounts = yaml .safe_load (stream )
127- if accounts is None : # Catch empty file
128- continue
129- for iri in accounts .keys ():
130- if (
131- "username" in accounts [iri ]
132- and "password" in accounts [iri ]
133- ):
134- cred = CredentialManager .UserPwdCredential (
135- username = accounts [iri ]["username" ],
136- password = accounts [iri ]["password" ],
137- iri = iri ,
138- )
139- _file_credentials .append (cred )
140- if (
141- "consumer_token" in accounts [iri ]
142- and "consumer_secret" in accounts [iri ]
143- and "access_token" in accounts [iri ]
144- and "access_secret" in accounts [iri ]
145- ):
146- cred = CredentialManager .OAuth1Credential (
147- consumer_token = accounts [iri ]["consumer_token" ],
148- consumer_secret = accounts [iri ]["consumer_secret" ],
149- access_token = accounts [iri ]["access_token" ],
150- access_secret = accounts [iri ]["access_secret" ],
151- iri = iri ,
152- )
153- _file_credentials .append (cred )
154- except yaml .YAMLError as exc :
155- print (exc )
156-
157- match_iri = ""
158- cred = None
159- creds = _file_credentials + self ._credentials
160- for _cred in creds :
161- iri = _cred .iri
162- if config .iri in iri :
163- if match_iri == "" or len (match_iri ) > len (
164- iri
165- ): # use the less specific match
166- match_iri = iri
167- cred = _cred
168-
169- if cred is None :
170- if config .fallback is CredentialManager .CredentialFallback .ask :
171- if self .cred_filepath :
172- filepath_str = "', '" .join ([str (fp ) for fp in self .cred_filepath ])
173- print (
174- f"No credentials for { config .iri } found in path '{ filepath_str } '. "
175- f"Please use the prompt to login"
176- )
177- username = input ("Enter username: " )
178- password = getpass .getpass ("Enter password: " )
179- cred = CredentialManager .UserPwdCredential (
180- username = username , password = password , iri = config .iri
156+ match = _find_credential (config .iri , oold_creds )
157+
158+ if match is not None :
159+ if isinstance (match , CredentialManager .BaseCredential ):
160+ return match
161+ return self ._oold_to_osw (match )
162+
163+ if config .fallback is CredentialManager .CredentialFallback .ask :
164+ if self .cred_filepath :
165+ filepath_str = "', '" .join ([str (fp ) for fp in self .cred_filepath ])
166+ print (
167+ f"No credentials for { config .iri } found in path '{ filepath_str } '. "
168+ f"Please use the prompt to login"
181169 )
182- self .add_credential (cred )
183- if self .cred_filepath :
184- self .save_credentials_to_file ()
185- return cred
170+ username = input ("Enter username: " )
171+ password = getpass .getpass ("Enter password: " )
172+ cred = CredentialManager .UserPwdCredential (
173+ username = username , password = password , iri = config .iri
174+ )
175+ self .add_credential (cred )
176+ if self .cred_filepath :
177+ self .save_credentials_to_file ()
178+ return cred
179+
180+ return None
186181
187182 def add_credential (self , cred : BaseCredential ):
188183 """adds a credential to the in memory store
@@ -232,7 +227,7 @@ def iri_in_file(self, iri: str) -> bool:
232227 with open (fp , "r" , encoding = "utf-8" ) as stream :
233228 try :
234229 accounts = yaml .safe_load (stream )
235- if accounts is None : # Catch empty file
230+ if accounts is None :
236231 continue
237232 for iri_ in accounts .keys ():
238233 if iri_ == iri :
@@ -243,7 +238,7 @@ def iri_in_file(self, iri: str) -> bool:
243238
244239 def save_credentials_to_file (
245240 self ,
246- filepath : Union [str , PossibleFilePath ] = None ,
241+ filepath : Union [str , Path ] = None ,
247242 set_cred_filepath : bool = False ,
248243 ):
249244 """Saves the in memory credentials to a file
@@ -259,13 +254,11 @@ def save_credentials_to_file(
259254 cred_filepath of the CredentialManager is not changed.
260255 """
261256 cred_filepaths = [filepath ]
262- """The filepath to save the credentials to."""
263257 if filepath is None :
264258 cred_filepaths = self .cred_filepath
265259 if self .cred_filepath is None :
266260 cred_filepaths = [default_paths .cred_filepath ]
267261 if set_cred_filepath :
268- # Creates error if file does not exist -> Using custom FilePath
269262 self .cred_filepath = cred_filepaths
270263 for fp in cred_filepaths :
271264 file = Path (fp )
@@ -275,7 +268,7 @@ def save_credentials_to_file(
275268 file_already_exists = file .exists ()
276269 if file_already_exists :
277270 data = yaml .safe_load (file .read_text (encoding = "utf-8" ))
278- if data is None : # Catch empty file
271+ if data is None :
279272 data = {}
280273 for cred in self ._credentials :
281274 data [cred .iri ] = cred .dict (exclude = {"iri" })
@@ -316,7 +309,6 @@ def save_credentials_to_file(
316309 f"'{ gitignore_fp } '."
317310 )
318311 containing_gitignore = gitignore_fp .parent .absolute ()
319-
320312 if containing_gitignore in default_paths .osw_files_dir .parents :
321313 # If the default_path.osw_files_dir is a subdirectory of the directory
322314 # containing the .gitignore file, add the relative path to the
0 commit comments