github.com/cozy/cozy-stack@v0.0.0-20240603063001-31110fa4cae1/web/instances/client.go (about)

     1  package instances
     2  
     3  import (
     4  	"net/http"
     5  	"strconv"
     6  	"time"
     7  
     8  	"github.com/cozy/cozy-stack/model/instance"
     9  	"github.com/cozy/cozy-stack/model/instance/lifecycle"
    10  	"github.com/cozy/cozy-stack/model/oauth"
    11  	"github.com/cozy/cozy-stack/pkg/consts"
    12  	"github.com/cozy/cozy-stack/pkg/couchdb"
    13  	"github.com/cozy/cozy-stack/pkg/logger"
    14  	"github.com/labstack/echo/v4"
    15  )
    16  
    17  func createToken(c echo.Context) error {
    18  	domain := c.QueryParam("Domain")
    19  	audience := c.QueryParam("Audience")
    20  	scope := c.QueryParam("Scope")
    21  	subject := c.QueryParam("Subject")
    22  	in, err := lifecycle.GetInstance(domain)
    23  	if err != nil {
    24  		// With a cluster of couchdb, we can have a race condition where we
    25  		// query an index before it has been updated for an instance that has
    26  		// just been created.
    27  		// Cf https://issues.apache.org/jira/browse/COUCHDB-3336
    28  		time.Sleep(1 * time.Second)
    29  		in, err = lifecycle.GetInstance(domain)
    30  		if err != nil {
    31  			return wrapError(err)
    32  		}
    33  	}
    34  	issuedAt := time.Now()
    35  	validity := consts.DefaultValidityDuration
    36  	switch audience {
    37  	case consts.AppAudience, "webapp":
    38  		audience = consts.AppAudience
    39  		validity = consts.AppTokenValidityDuration
    40  	case consts.KonnectorAudience, "konnector":
    41  		audience = consts.KonnectorAudience
    42  		validity = consts.KonnectorTokenValidityDuration
    43  	case consts.AccessTokenAudience, "access-token":
    44  		if err := checkClient(in, subject); err != nil {
    45  			return err
    46  		}
    47  		audience = consts.AccessTokenAudience
    48  		validity = consts.AccessTokenValidityDuration
    49  	case consts.RefreshTokenAudience, "refresh-token":
    50  		if err := checkClient(in, subject); err != nil {
    51  			return err
    52  		}
    53  		audience = consts.RefreshTokenAudience
    54  	case consts.CLIAudience:
    55  		audience = consts.CLIAudience
    56  		validity = consts.CLITokenValidityDuration
    57  	default:
    58  		return echo.NewHTTPError(http.StatusBadRequest, "Unknown audience %s", audience)
    59  	}
    60  	if e := c.QueryParam("Expire"); e != "" && e != "0s" {
    61  		var d time.Duration
    62  		if d, err = time.ParseDuration(e); err == nil {
    63  			issuedAt = issuedAt.Add(d - validity)
    64  		}
    65  	}
    66  	token, err := in.MakeJWT(audience, subject, scope, "", issuedAt)
    67  	if err != nil {
    68  		return err
    69  	}
    70  	logger.WithDomain(domain).WithNamespace("loginaudit").
    71  		Infof("%s token created from admin API at %s", audience, issuedAt)
    72  	return c.String(http.StatusOK, token)
    73  }
    74  
    75  func checkClient(inst *instance.Instance, clientID string) error {
    76  	client, err := oauth.FindClient(inst, clientID)
    77  	if err != nil {
    78  		return err
    79  	}
    80  	if client.Pending {
    81  		client.Pending = false
    82  		_ = couchdb.UpdateDoc(inst, client)
    83  	}
    84  	return nil
    85  }
    86  
    87  func registerClient(c echo.Context) error {
    88  	in, err := lifecycle.GetInstance(c.QueryParam("Domain"))
    89  	if err != nil {
    90  		return wrapError(err)
    91  	}
    92  	allowLoginScope, err := strconv.ParseBool(c.QueryParam("AllowLoginScope"))
    93  	if err != nil {
    94  		return wrapError(err)
    95  	}
    96  
    97  	client := oauth.Client{
    98  		RedirectURIs:    []string{c.QueryParam("RedirectURI")},
    99  		ClientName:      c.QueryParam("ClientName"),
   100  		SoftwareID:      c.QueryParam("SoftwareID"),
   101  		AllowLoginScope: allowLoginScope,
   102  	}
   103  	if regErr := client.Create(in, oauth.NotPending); regErr != nil {
   104  		return c.String(http.StatusBadRequest, regErr.Description)
   105  	}
   106  	return c.JSON(http.StatusOK, client)
   107  }
   108  
   109  func findClientBySoftwareID(c echo.Context) error {
   110  	domain := c.QueryParam("domain")
   111  	softwareID := c.QueryParam("software_id")
   112  
   113  	inst, err := lifecycle.GetInstance(domain)
   114  	if err != nil {
   115  		return err
   116  	}
   117  	client, err := oauth.FindClientBySoftwareID(inst, softwareID)
   118  	if err != nil {
   119  		return err
   120  	}
   121  	return c.JSON(http.StatusOK, client)
   122  }