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