1717
1818import java .lang .reflect .InvocationTargetException ;
1919import java .util .Arrays ;
20+ import java .util .Objects ;
2021import java .util .Optional ;
22+ import java .util .function .Supplier ;
2123import software .amazon .awssdk .annotations .SdkInternalApi ;
2224import software .amazon .awssdk .auth .token .credentials .ChildProfileTokenProviderFactory ;
2325import software .amazon .awssdk .auth .token .credentials .SdkTokenProvider ;
26+ import software .amazon .awssdk .core .exception .SdkClientException ;
2427import software .amazon .awssdk .core .internal .util .ClassLoaderHelper ;
2528import software .amazon .awssdk .profiles .Profile ;
2629import software .amazon .awssdk .profiles .ProfileFile ;
2730import software .amazon .awssdk .profiles .ProfileProperty ;
2831import software .amazon .awssdk .profiles .internal .ProfileSection ;
32+ import software .amazon .awssdk .utils .Lazy ;
2933import software .amazon .awssdk .utils .Validate ;
3034
3135/**
@@ -36,12 +40,33 @@ public final class ProfileTokenProviderLoader {
3640 private static final String SSO_OIDC_TOKEN_PROVIDER_FACTORY =
3741 "software.amazon.awssdk.services.ssooidc.SsoOidcProfileTokenProviderFactory" ;
3842
43+ private final boolean createProviderFromSupplier ;
44+
3945 private final Profile profile ;
4046 private final ProfileFile profileFile ;
47+ private final Supplier <ProfileFile > profileFileSupplier ;
48+ private final String profileName ;
49+ private volatile ProfileFile currentProfileFile ;
50+ private volatile SdkTokenProvider currentTokenProvider ;
51+
52+ private final Lazy <ChildProfileTokenProviderFactory > factory ;
4153
42- public ProfileTokenProviderLoader (ProfileFile profileFile , Profile profile ) {
43- this .profile = Validate . paramNotNull ( profile , "profile" ) ;
54+ public ProfileTokenProviderLoader (ProfileFile profileFile , String profileName ) {
55+ this .createProviderFromSupplier = false ;
4456 this .profileFile = Validate .paramNotNull (profileFile , "profileFile" );
57+ this .profileFileSupplier = null ;
58+ this .profileName = Validate .paramNotNull (profileName , "profileName" );
59+ this .profile = resolveProfile (profileFile , profileName );
60+ this .factory = new Lazy <>(this ::ssoTokenProviderFactory );
61+ }
62+
63+ public ProfileTokenProviderLoader (Supplier <ProfileFile > profileFile , String profileName ) {
64+ createProviderFromSupplier = true ;
65+ this .profileFile = null ;
66+ this .profileFileSupplier = Validate .paramNotNull (profileFile , "profileFile" );
67+ this .profileName = Validate .paramNotNull (profileName , "profileName" );
68+ this .profile = null ;
69+ this .factory = new Lazy <>(this ::ssoTokenProviderFactory );
4570 }
4671
4772 /**
@@ -55,19 +80,53 @@ public Optional<SdkTokenProvider> tokenProvider() {
5580 * Create the SSO credentials provider based on the related profile properties.
5681 */
5782 private SdkTokenProvider ssoProfileCredentialsProvider () {
83+ if (createProviderFromSupplier ) {
84+ return () -> ssoProfileCredentialsProvider (profileFileSupplier , profileName ).resolveToken ();
85+ }
5886
59- String profileSsoSectionName = profile .property (ProfileSection .SSO_SESSION .getPropertyKeyName ())
60- .orElseThrow (() -> new IllegalArgumentException (
61- "Profile " + profile .name () + " does not have sso_session property" ));
87+ return ssoProfileCredentialsProvider (profileFile , profile );
88+ }
89+
90+ private SdkTokenProvider ssoProfileCredentialsProvider (ProfileFile profileFile , Profile profile ) {
91+ String profileSsoSectionName = profileSsoSectionName (profile );
92+ Profile ssoProfile = ssoProfile (profileFile , profileSsoSectionName );
6293
63- Profile ssoProfile = profileFile .getSection (ProfileSection .SSO_SESSION .getSectionTitle (), profileSsoSectionName )
64- .orElseThrow (() -> new IllegalArgumentException (
65- "Sso-session section not found with sso-session title " + profileSsoSectionName ));
94+ validateRequiredProperties (ssoProfile , ProfileProperty .SSO_REGION , ProfileProperty .SSO_START_URL );
95+
96+ return factory .getValue ().create (profileFile , profile );
97+ }
98+
99+ private synchronized SdkTokenProvider ssoProfileCredentialsProvider (Supplier <ProfileFile > profileFile , String profileName ) {
100+ ProfileFile profileFileInstance = profileFile .get ();
101+ Profile profileInstance = resolveProfile (profileFileInstance , profileName );
102+ if (!Objects .equals (profileFileInstance , currentProfileFile )) {
103+ currentProfileFile = profileFileInstance ;
104+ currentTokenProvider = ssoProfileCredentialsProvider (profileFileInstance , profileInstance );
105+ }
106+
107+ return currentTokenProvider ;
108+ }
109+
110+ private Profile resolveProfile (ProfileFile profileFile , String profileName ) {
111+ return profileFile .profile (profileName )
112+ .orElseThrow (() -> {
113+ String errorMessage = String .format ("Profile file contained no information for profile '%s': %s" ,
114+ profileName , profileFile );
115+ return SdkClientException .builder ().message (errorMessage ).build ();
116+ });
117+ }
118+
119+ private String profileSsoSectionName (Profile profile ) {
120+ return Optional .ofNullable (profile )
121+ .flatMap (p -> p .property (ProfileSection .SSO_SESSION .getPropertyKeyName ()))
122+ .orElseThrow (() -> new IllegalArgumentException (
123+ "Profile " + profileName + " does not have sso_session property" ));
124+ }
66125
67- validateRequiredProperties ( ssoProfile ,
68- ProfileProperty . SSO_REGION ,
69- ProfileProperty . SSO_START_URL );
70- return ssoTokenProviderFactory (). create ( profileFile , profile );
126+ private Profile ssoProfile ( ProfileFile profileFile , String profileSsoSectionName ) {
127+ return profileFile . getSection ( ProfileSection . SSO_SESSION . getSectionTitle (), profileSsoSectionName )
128+ . orElseThrow (() -> new IllegalArgumentException (
129+ "Sso-session section not found with sso-session title " + profileSsoSectionName ) );
71130 }
72131
73132 /**
@@ -76,7 +135,8 @@ private SdkTokenProvider ssoProfileCredentialsProvider() {
76135 private void validateRequiredProperties (Profile ssoProfile , String ... requiredProperties ) {
77136 Arrays .stream (requiredProperties )
78137 .forEach (p -> Validate .isTrue (ssoProfile .properties ().containsKey (p ),
79- "Property '%s' was not configured for profile '%s'." , p , this .profile .name ()));
138+ "Property '%s' was not configured for profile '%s'." ,
139+ p , profileName ));
80140 }
81141
82142 /**
@@ -88,10 +148,10 @@ private ChildProfileTokenProviderFactory ssoTokenProviderFactory() {
88148 getClass ());
89149 return (ChildProfileTokenProviderFactory ) ssoOidcTokenProviderFactory .getConstructor ().newInstance ();
90150 } catch (ClassNotFoundException e ) {
91- throw new IllegalStateException ("To use SSO OIDC related properties in the '" + profile . name () + "' profile, "
151+ throw new IllegalStateException ("To use SSO OIDC related properties in the '" + profileName + "' profile, "
92152 + "the 'ssooidc' service module must be on the class path." , e );
93153 } catch (NoSuchMethodException | InvocationTargetException | InstantiationException | IllegalAccessException e ) {
94- throw new IllegalStateException ("Failed to create the '" + profile . name () + "' token provider factory." , e );
154+ throw new IllegalStateException ("Failed to create the '%s " + profileName + "' token provider factory." , e );
95155 }
96156 }
97157}
0 commit comments