@@ -24,6 +24,11 @@ import { CredentialsProviderManager } from './providers/credentialsProviderManag
24
24
import { asString , CredentialsId , CredentialsProvider , fromString } from './providers/credentials'
25
25
import { once } from '../shared/utilities/functionUtils'
26
26
import { CredentialsSettings } from './credentials/utils'
27
+ import {
28
+ extractDataFromSection ,
29
+ getSectionOrThrow ,
30
+ loadSharedCredentialsSections ,
31
+ } from './credentials/sharedCredentials'
27
32
import { getCodeCatalystDevEnvId } from '../shared/vscode/env'
28
33
import { partition } from '../shared/utilities/mementos'
29
34
import { SsoCredentialsProvider } from './providers/ssoCredentialsProvider'
@@ -47,8 +52,9 @@ import {
47
52
StoredProfile ,
48
53
codecatalystScopes ,
49
54
createBuilderIdProfile ,
55
+ createSsoProfile ,
50
56
hasScopes ,
51
- isBuilderIdConnection ,
57
+ isValidCodeCatalystConnection ,
52
58
loadIamProfilesIntoStore ,
53
59
loadLinkedProfilesIntoStore ,
54
60
ssoAccountAccessScopes ,
@@ -521,9 +527,11 @@ export class Auth implements AuthService, ConnectionManager {
521
527
}
522
528
523
529
// XXX: always read from the same location in a dev environment
524
- private getSsoSessionName = once ( ( ) => {
530
+ // This detection is fuzzy if an sso-session section exists for any other reason.
531
+ private detectSsoSessionNameForCodeCatalyst = once ( ( ) : string => {
525
532
try {
526
533
const configFile = getConfigFilename ( )
534
+ // `require('fs')` is workaround for web mode:
527
535
const contents : string = require ( 'fs' ) . readFileSync ( configFile , 'utf-8' )
528
536
const identifier = contents . match ( / \[ s s o \- s e s s i o n ( .* ) \] / ) ?. [ 1 ]
529
537
if ( ! identifier ) {
@@ -532,21 +540,40 @@ export class Auth implements AuthService, ConnectionManager {
532
540
533
541
return identifier
534
542
} catch ( err ) {
535
- const defaultName = 'codecatalyst'
536
- getLogger ( ) . warn ( `auth: unable to get an sso session name, defaulting to "${ defaultName } ": %s` , err )
537
-
538
- return defaultName
543
+ const identifier = 'codecatalyst'
544
+ getLogger ( ) . warn ( `auth: unable to get an sso session name, defaulting to "${ identifier } ": %s` , err )
545
+ return identifier
539
546
}
540
547
} )
541
548
549
+ private createCodeCatalystDevEnvProfile = async ( ) : Promise < [ id : string , profile : SsoProfile ] > => {
550
+ const identifier = this . detectSsoSessionNameForCodeCatalyst ( )
551
+
552
+ const { sections } = await loadSharedCredentialsSections ( )
553
+ const { sso_region : region , sso_start_url : startUrl } = extractDataFromSection (
554
+ getSectionOrThrow ( sections , identifier , 'sso-session' )
555
+ )
556
+
557
+ if ( [ region , startUrl ] . some ( prop => typeof prop !== 'string' ) ) {
558
+ throw new ToolkitError ( 'sso-session data missing in ~/.aws/config' , { code : 'NoSsoSession' } )
559
+ }
560
+
561
+ return startUrl === builderIdStartUrl
562
+ ? [ identifier , createBuilderIdProfile ( codecatalystScopes ) ]
563
+ : [ identifier , createSsoProfile ( region , startUrl , codecatalystScopes ) ]
564
+ }
565
+
542
566
private getTokenProvider ( id : Connection [ 'id' ] , profile : StoredProfile < SsoProfile > ) {
543
- // XXX: Use the token created by dev environments if and only if the profile is strictly for CodeCatalyst
567
+ // XXX: Use the token created by Dev Environments if and only if the profile is strictly
568
+ // for CodeCatalyst, as indicated by its set of scopes. A consequence of these semantics is
569
+ // that any profile will be coerced to use this token if that profile exclusively contains
570
+ // CodeCatalyst scopes. Similarly, if additional scopes are added to a profile, the profile
571
+ // no longer matches this condition.
544
572
const shouldUseSoftwareStatement =
545
573
getCodeCatalystDevEnvId ( ) !== undefined &&
546
- profile . startUrl === builderIdStartUrl &&
547
574
profile . scopes ?. every ( scope => codecatalystScopes . includes ( scope ) )
548
575
549
- const tokenIdentifier = shouldUseSoftwareStatement ? this . getSsoSessionName ( ) : id
576
+ const tokenIdentifier = shouldUseSoftwareStatement ? this . detectSsoSessionNameForCodeCatalyst ( ) : id
550
577
551
578
return this . createTokenProvider (
552
579
{
@@ -702,15 +729,28 @@ export class Auth implements AuthService, ConnectionManager {
702
729
} )
703
730
)
704
731
705
- // Use the environment token if available
706
- // This token only has CC permissions currently!
732
+ // When opening a Dev Environment, use the environment token if no other CodeCatalyst
733
+ // credential is in use. This token only has CC permissions currently!
707
734
if ( getCodeCatalystDevEnvId ( ) !== undefined ) {
708
- const connections = ( await this . listConnections ( ) ) . filter ( isBuilderIdConnection )
709
-
710
- if ( connections . length === 0 ) {
711
- const key = uuid . v4 ( )
712
- await this . store . addProfile ( key , createBuilderIdProfile ( codecatalystScopes ) )
713
- await this . store . setCurrentProfileId ( key )
735
+ const connections = await this . listConnections ( )
736
+ const shouldInsertDevEnvCredential = ! connections . some ( isValidCodeCatalystConnection )
737
+
738
+ if ( shouldInsertDevEnvCredential ) {
739
+ // Insert a profile based on the `~/.aws/config` sso-session:
740
+ try {
741
+ // After creating a CodeCatalyst Dev Environment profile based on sso-session,
742
+ // we discard the actual key, and we insert the profile with an arbitrary key.
743
+ // The cache key for any strictly-CodeCatalyst profile is overriden to the
744
+ // sso-session identifier on read. If the coerce-on-read semantics ever become
745
+ // an issue, it should be possible to use the actual key here However, this
746
+ // would require deleting existing profiles to avoid inserting duplicates.
747
+ const [ _dangerousDiscardActualKey , devEnvProfile ] = await this . createCodeCatalystDevEnvProfile ( )
748
+ const key = uuid . v4 ( )
749
+ await this . store . addProfile ( key , devEnvProfile )
750
+ await this . store . setCurrentProfileId ( key )
751
+ } catch ( err ) {
752
+ getLogger ( ) . warn ( `auth: failed to insert dev env profile: %s` , err )
753
+ }
714
754
}
715
755
}
716
756
0 commit comments