identity.go raw

   1  package internal
   2  
   3  import (
   4  	"context"
   5  	"encoding/json"
   6  	"errors"
   7  	"io"
   8  	"net/http"
   9  	"net/url"
  10  
  11  	"github.com/go-acme/lego/v4/providers/dns/internal/errutils"
  12  )
  13  
  14  // DefaultIdentityEndpoint the default API identity endpoint.
  15  const DefaultIdentityEndpoint = "https://iam.eu-de.otc.t-systems.com:443/v3/auth/tokens"
  16  
  17  // Login Starts a new OTC API Session. Authenticates using userName, password
  18  // and receives a token to be used in for subsequent requests.
  19  func (c *Client) Login(ctx context.Context) error {
  20  	payload := LoginRequest{
  21  		Auth: Auth{
  22  			Identity: Identity{
  23  				Methods: []string{"password"},
  24  				Password: Password{
  25  					User: User{
  26  						Name:     c.username,
  27  						Password: c.password,
  28  						Domain: Domain{
  29  							Name: c.domainName,
  30  						},
  31  					},
  32  				},
  33  			},
  34  			Scope: Scope{
  35  				Project: Project{
  36  					Name: c.projectName,
  37  				},
  38  			},
  39  		},
  40  	}
  41  
  42  	tokenResp, token, err := c.obtainUserToken(ctx, payload)
  43  	if err != nil {
  44  		return err
  45  	}
  46  
  47  	c.muToken.Lock()
  48  	defer c.muToken.Unlock()
  49  
  50  	c.token = token
  51  
  52  	if c.token == "" {
  53  		return errors.New("unable to get auth token")
  54  	}
  55  
  56  	baseURL, err := getBaseURL(tokenResp)
  57  	if err != nil {
  58  		return err
  59  	}
  60  
  61  	c.muBaseURL.Lock()
  62  	c.baseURL = baseURL
  63  	c.muBaseURL.Unlock()
  64  
  65  	return nil
  66  }
  67  
  68  // https://docs.otc.t-systems.com/identity-access-management/api-ref/apis/token_management/obtaining_a_user_token.html
  69  func (c *Client) obtainUserToken(ctx context.Context, payload LoginRequest) (*TokenResponse, string, error) {
  70  	req, err := newJSONRequest(ctx, http.MethodPost, c.IdentityEndpoint, payload)
  71  	if err != nil {
  72  		return nil, "", err
  73  	}
  74  
  75  	client := &http.Client{Timeout: c.HTTPClient.Timeout}
  76  
  77  	resp, err := client.Do(req)
  78  	if err != nil {
  79  		return nil, "", err
  80  	}
  81  
  82  	defer func() { _ = resp.Body.Close() }()
  83  
  84  	if resp.StatusCode/100 != 2 {
  85  		return nil, "", errutils.NewUnexpectedResponseStatusCodeError(req, resp)
  86  	}
  87  
  88  	token := resp.Header.Get("X-Subject-Token")
  89  
  90  	if token == "" {
  91  		return nil, "", errors.New("unable to get auth token")
  92  	}
  93  
  94  	raw, err := io.ReadAll(resp.Body)
  95  	if err != nil {
  96  		return nil, "", errutils.NewReadResponseError(req, resp.StatusCode, err)
  97  	}
  98  
  99  	var newToken TokenResponse
 100  
 101  	err = json.Unmarshal(raw, &newToken)
 102  	if err != nil {
 103  		return nil, "", errutils.NewUnmarshalError(req, resp.StatusCode, raw, err)
 104  	}
 105  
 106  	return &newToken, token, nil
 107  }
 108  
 109  func getBaseURL(tokenResp *TokenResponse) (*url.URL, error) {
 110  	var endpoints []Endpoint
 111  
 112  	for _, v := range tokenResp.Token.Catalog {
 113  		if v.Type == "dns" {
 114  			endpoints = append(endpoints, v.Endpoints...)
 115  		}
 116  	}
 117  
 118  	if len(endpoints) == 0 {
 119  		return nil, errors.New("unable to get dns endpoint")
 120  	}
 121  
 122  	baseURL, err := url.JoinPath(endpoints[0].URL, "v2")
 123  	if err != nil {
 124  		return nil, err
 125  	}
 126  
 127  	return url.Parse(baseURL)
 128  }
 129