identity.go raw

   1  package internal
   2  
   3  import (
   4  	"bytes"
   5  	"context"
   6  	"encoding/json"
   7  	"fmt"
   8  	"net/http"
   9  	"strings"
  10  	"time"
  11  
  12  	"github.com/go-acme/lego/v4/providers/dns/internal/errutils"
  13  )
  14  
  15  // authEndpoint represents the Identity API endpoint to call.
  16  const authEndpoint = "https://kasapi.kasserver.com/soap/KasAuth.php"
  17  
  18  type token string
  19  
  20  const tokenKey token = "token"
  21  
  22  // Identifier generates credential tokens.
  23  type Identifier struct {
  24  	login    string
  25  	password string
  26  
  27  	authEndpoint string
  28  	HTTPClient   *http.Client
  29  }
  30  
  31  // NewIdentifier creates a new Identifier.
  32  func NewIdentifier(login, password string) *Identifier {
  33  	return &Identifier{
  34  		login:        login,
  35  		password:     password,
  36  		authEndpoint: authEndpoint,
  37  		HTTPClient:   &http.Client{Timeout: 10 * time.Second},
  38  	}
  39  }
  40  
  41  // Authentication Creates a credential token.
  42  // - sessionLifetime: Validity of the token in seconds.
  43  // - sessionUpdateLifetime: with `true` the session is extended with every request.
  44  func (c *Identifier) Authentication(ctx context.Context, sessionLifetime int, sessionUpdateLifetime bool) (string, error) {
  45  	sul := "N"
  46  	if sessionUpdateLifetime {
  47  		sul = "Y"
  48  	}
  49  
  50  	ar := AuthRequest{
  51  		Login:                 c.login,
  52  		AuthData:              c.password,
  53  		AuthType:              "plain",
  54  		SessionLifetime:       sessionLifetime,
  55  		SessionUpdateLifetime: sul,
  56  	}
  57  
  58  	body, err := json.Marshal(ar)
  59  	if err != nil {
  60  		return "", fmt.Errorf("failed to create request JSON body: %w", err)
  61  	}
  62  
  63  	payload := []byte(strings.TrimSpace(fmt.Sprintf(kasAuthEnvelope, body)))
  64  
  65  	req, err := http.NewRequestWithContext(ctx, http.MethodPost, c.authEndpoint, bytes.NewReader(payload))
  66  	if err != nil {
  67  		return "", fmt.Errorf("unable to create request: %w", err)
  68  	}
  69  
  70  	resp, err := c.HTTPClient.Do(req)
  71  	if err != nil {
  72  		return "", errutils.NewHTTPDoError(req, err)
  73  	}
  74  
  75  	defer func() { _ = resp.Body.Close() }()
  76  
  77  	if resp.StatusCode != http.StatusOK {
  78  		return "", errutils.NewUnexpectedResponseStatusCodeError(req, resp)
  79  	}
  80  
  81  	envlp, err := decodeXML[KasAuthEnvelope](resp.Body)
  82  	if err != nil {
  83  		return "", err
  84  	}
  85  
  86  	if envlp.Body.Fault != nil {
  87  		return "", envlp.Body.Fault
  88  	}
  89  
  90  	return envlp.Body.KasAuthResponse.Return.Text, nil
  91  }
  92  
  93  func WithContext(ctx context.Context, credential string) context.Context {
  94  	return context.WithValue(ctx, tokenKey, credential)
  95  }
  96  
  97  func getToken(ctx context.Context) string {
  98  	credential, ok := ctx.Value(tokenKey).(string)
  99  	if !ok {
 100  		return ""
 101  	}
 102  
 103  	return credential
 104  }
 105