11package auth
22
33import (
4+ "bytes"
45 "context"
6+ "encoding/json"
57 "fmt"
8+ "net/http"
69 "os"
710 "path"
11+ "strings"
812 "time"
913
1014 "github.com/golang-jwt/jwt/v5"
1115 "github.com/magaluCloud/mgccli/cmd/common/structs"
1216 "github.com/magaluCloud/mgccli/cmd/common/workspace"
17+ cmdutils "github.com/magaluCloud/mgccli/cmd_utils"
1318 "gopkg.in/yaml.v3"
1419)
1520
@@ -32,17 +37,23 @@ type Auth interface {
3237 GetRefreshToken () string
3338 GetSecretAccessKey () string
3439 GetService () * Service
40+ GetCurrentTenantID () (string , error )
41+ GetCurrentTenant (ctx context.Context ) (* Tenant , error )
42+ GetScopes () (string , error )
3543 TokenClaims () (* TokenClaims , error )
3644
3745 SetAccessToken (token string ) error
3846 SetRefreshToken (token string ) error
3947 SetSecretAccessKey (key string ) error
4048 SetAccessKeyID (key string ) error
49+ SetTenant (ctx context.Context , id string ) (* TokenExchangeResult , error )
4150
4251 ValidateToken () error
4352 RefreshToken (ctx context.Context ) error
4453
4554 Logout () error
55+
56+ ListTenants (ctx context.Context ) ([]* Tenant , error )
4657}
4758
4859type authValue struct {
@@ -98,6 +109,54 @@ func (a *authValue) GetSecretAccessKey() string {
98109 return a .authValue .SecretAccessKey
99110}
100111
112+ func (a * authValue ) GetCurrentTenantID () (string , error ) {
113+ claims , err := a .TokenClaims ()
114+ if err != nil {
115+ return "" , err
116+ }
117+
118+ tenantId := claims .TenantIDWithType
119+
120+ // Dot is a separator, Tenant will be <TenantType>.<ID>. We only want the ID
121+ dotIndex := strings .Index (tenantId , "." )
122+
123+ if dotIndex != - 1 {
124+ tenantId = tenantId [dotIndex + 1 :]
125+ }
126+
127+ return tenantId , nil
128+ }
129+
130+ func (a * authValue ) GetCurrentTenant (ctx context.Context ) (* Tenant , error ) {
131+ currentTenantId , err := a .GetCurrentTenantID ()
132+ if err != nil {
133+ return nil , err
134+ }
135+
136+ tenants , err := a .ListTenants (ctx )
137+ if err != nil || len (tenants ) == 0 {
138+ fmt .Printf ("Não foi possível pegar as informações sobre o tenant atual, retornando apenas o ID.\n Erro: %v\n \n " , err )
139+ return & Tenant {UUID : currentTenantId }, nil
140+ }
141+
142+ for _ , tenant := range tenants {
143+ if tenant .UUID == currentTenantId {
144+ return tenant , nil
145+ }
146+ }
147+
148+ return nil , fmt .Errorf ("o ID (%s) do tenant atual não foi encontrado na lista de tenants" , currentTenantId )
149+ }
150+
151+ func (a * authValue ) GetScopes () (string , error ) {
152+ tokenClaims , err := a .TokenClaims ()
153+ if err != nil {
154+ return "" , err
155+ }
156+
157+ return tokenClaims .ScopesStr , nil
158+ }
159+
101160func (a * authValue ) SetAccessToken (token string ) error {
102161 a .authValue .AccessToken = token
103162 return a .Write ()
@@ -182,3 +241,119 @@ func (a *authValue) RefreshToken(ctx context.Context) error {
182241 a .authValue .RefreshToken = token .RefreshToken
183242 return a .Write ()
184243}
244+
245+ func (a * authValue ) ListTenants (ctx context.Context ) ([]* Tenant , error ) {
246+ client , err := NewOAuthClient (a .service .config )
247+ if err != nil {
248+ return nil , fmt .Errorf ("failed to create OAuth client: %w" , err )
249+ }
250+
251+ httpClient := client .AuthenticatedHttpClientFromContext (ctx )
252+ if httpClient == nil {
253+ return nil , fmt .Errorf ("programming error: unable to get HTTP Client from context" )
254+ }
255+
256+ r , err := http .NewRequestWithContext (
257+ ctx ,
258+ http .MethodGet ,
259+ a .service .config .TenantsListURL ,
260+ nil ,
261+ )
262+ if err != nil {
263+ return nil , err
264+ }
265+ r .Header .Set ("Content-Type" , "application/json" )
266+
267+ resp , err := httpClient .Do (r )
268+ if err != nil {
269+ return nil , err
270+ }
271+
272+ if resp .StatusCode != http .StatusOK {
273+ return nil , cmdutils .NewHttpErrorFromResponse (resp , r )
274+ }
275+
276+ defer resp .Body .Close ()
277+ var result []* Tenant
278+ if err = json .NewDecoder (resp .Body ).Decode (& result ); err != nil {
279+ return nil , err
280+ }
281+
282+ return result , nil
283+ }
284+
285+ func (a * authValue ) SetTenant (ctx context.Context , id string ) (* TokenExchangeResult , error ) {
286+ return a .runTokenExchange (ctx , id )
287+ }
288+
289+ func (a * authValue ) runTokenExchange (
290+ ctx context.Context , tenantId string ,
291+ ) (* TokenExchangeResult , error ) {
292+ client , err := NewOAuthClient (a .service .config )
293+ if err != nil {
294+ return nil , fmt .Errorf ("failed to create OAuth client: %w" , err )
295+ }
296+
297+ httpClient := client .AuthenticatedHttpClientFromContext (ctx )
298+ if httpClient == nil {
299+ return nil , fmt .Errorf ("programming error: unable to get HTTP Client from context" )
300+ }
301+
302+ scopes , err := a .GetScopes ()
303+ if err != nil {
304+ return nil , err
305+ }
306+
307+ data := map [string ]any {
308+ "tenant" : tenantId ,
309+ "scopes" : scopes ,
310+ }
311+ jsonData , err := json .Marshal (data )
312+ if err != nil {
313+ return nil , err
314+ }
315+
316+ bodyReader := bytes .NewReader (jsonData )
317+ r , err := http .NewRequestWithContext (ctx , http .MethodPost , a .service .config .TokenExchangeURL , bodyReader )
318+ r .Header .Set ("Content-Type" , "application/json" )
319+
320+ if err != nil {
321+ return nil , err
322+ }
323+
324+ resp , err := httpClient .Do (r )
325+ if err != nil {
326+ return nil , err
327+ }
328+
329+ defer r .Body .Close ()
330+
331+ if resp .StatusCode != http .StatusOK {
332+ return nil , cmdutils .NewHttpErrorFromResponse (resp , r )
333+ }
334+
335+ payload := & TenantResult {}
336+ if err = json .NewDecoder (resp .Body ).Decode (payload ); err != nil {
337+ return nil , err
338+ }
339+
340+ err = a .SetAccessToken (payload .AccessToken )
341+ if err != nil {
342+ return nil , err
343+ }
344+
345+ err = a .SetRefreshToken (payload .RefreshToken )
346+ if err != nil {
347+ return nil , err
348+ }
349+
350+ createdAt := time .Time (time .Unix (int64 (payload .CreatedAt ), 0 ))
351+
352+ return & TokenExchangeResult {
353+ AccessToken : payload .AccessToken ,
354+ CreatedAt : createdAt ,
355+ TenantID : tenantId ,
356+ RefreshToken : payload .RefreshToken ,
357+ Scope : strings .Split (payload .Scope , " " ),
358+ }, nil
359+ }
0 commit comments