@@ -18,12 +18,16 @@ package osclients
1818
1919import (
2020 "context"
21+ "errors"
2122 "fmt"
2223 "iter"
2324
25+ tokens3 "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/tokens"
26+
2427 "github.com/gophercloud/gophercloud/v2"
2528 "github.com/gophercloud/gophercloud/v2/openstack"
2629 "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/applicationcredentials"
30+ "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/users"
2731 "github.com/gophercloud/utils/v2/openstack/clientconfig"
2832)
2933
@@ -66,12 +70,69 @@ func (c applicationcredentialClient) DeleteApplicationCredential(ctx context.Con
6670 return applicationcredentials .Delete (ctx , c .client , resourceID ).ExtractErr ()
6771}
6872
73+ func (c applicationcredentialClient ) UpdateApplicationCredential (ctx context.Context , id string , opts applicationcredentials.UpdateOptsBuilder ) (* applicationcredentials.ApplicationCredential , error ) {
74+ return applicationcredentials .Update (ctx , c .client , id , opts ).Extract ()
75+ }
76+
6977func (c applicationcredentialClient ) GetApplicationCredential (ctx context.Context , resourceID string ) (* applicationcredentials.ApplicationCredential , error ) {
70- return applicationcredentials .Get (ctx , c .client , resourceID ).Extract ()
78+ // The unique ID of an application credential is not enough to query it from OpenStack
79+ // OpenStack actually also requires a unique user ID.
80+ // We can not provide the user ID here, as the function signatures of ORC interfaces
81+ // expect us to return an OpenStack resource based on a single string.
82+
83+ // To work around this, we first query ApplicationCredentials for the currently
84+ // authenticated user which ORC is connected as. If that fails, we iterate over
85+ // all users we have access to and query their ApplicationCredentials.
86+
87+ // Currently authenticated user
88+ userID , err := GetAuthenticatedUserID (c .client .ProviderClient )
89+ if err == nil {
90+ appCred , appCredErr := applicationcredentials .Get (ctx , c .client , userID , resourceID ).Extract ()
91+
92+ if appCred != nil {
93+ return appCred , appCredErr
94+ }
95+ }
96+
97+ // If not found in currently authenticated user, try iterating over all users
98+ userPager := users .List (c .client , nil )
99+ userIterator := func (yield func (* users.User , error ) bool ) {
100+ _ = userPager .EachPage (ctx , yieldPage (users .ExtractUsers , yield ))
101+ }
102+
103+ for user , userErr := range userIterator {
104+ if userErr != nil {
105+ continue
106+ }
107+
108+ appCred , appCredErr := applicationcredentials .Get (ctx , c .client , user .ID , resourceID ).Extract ()
109+
110+ if appCred != nil {
111+ return appCred , appCredErr
112+ }
113+ }
114+
115+ return nil , gophercloud.ErrResourceNotFound {
116+ Name : resourceID ,
117+ ResourceType : "ApplicationCredential" ,
118+ }
71119}
72120
73- func (c applicationcredentialClient ) UpdateApplicationCredential (ctx context.Context , id string , opts applicationcredentials.UpdateOptsBuilder ) (* applicationcredentials.ApplicationCredential , error ) {
74- return applicationcredentials .Update (ctx , c .client , id , opts ).Extract ()
121+ func GetAuthenticatedUserID (providerClient * gophercloud.ProviderClient ) (string , error ) {
122+ r := providerClient .GetAuthResult ()
123+ if r == nil {
124+ return "" , errors .New ("no AuthResult available" )
125+ }
126+ switch r := r .(type ) {
127+ case tokens3.CreateResult :
128+ u , err := r .ExtractUser ()
129+ if err != nil {
130+ return "" , err
131+ }
132+ return u .ID , nil
133+ default :
134+ return "" , errors .New ("wrong AuthResult version" )
135+ }
75136}
76137
77138type applicationcredentialErrorClient struct { error }
0 commit comments