github.com/qri-io/qri@v0.10.1-0.20220104210721-c771715036cb/lib/access.go (about)

     1  package lib
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"time"
     7  
     8  	"github.com/qri-io/qri/auth/token"
     9  	qhttp "github.com/qri-io/qri/lib/http"
    10  	"github.com/qri-io/qri/profile"
    11  )
    12  
    13  // AccessMethods is a group of methods for access control & user authentication
    14  type AccessMethods struct {
    15  	d dispatcher
    16  }
    17  
    18  // Name returns the name of this method group
    19  func (m AccessMethods) Name() string {
    20  	return "access"
    21  }
    22  
    23  // Attributes defines attributes for each method
    24  func (m AccessMethods) Attributes() map[string]AttributeSet {
    25  	return map[string]AttributeSet{
    26  		"createauthtoken": {Endpoint: qhttp.AECreateAuthToken, HTTPVerb: "POST", DefaultSource: "local"},
    27  	}
    28  }
    29  
    30  // CreateAuthTokenParams are input parameters for Access().CreateAuthToken
    31  type CreateAuthTokenParams struct {
    32  	// username to grant auth; e.g. "keyboard_cat"
    33  	GranteeUsername string `json:"granteeUsername"`
    34  	// profile Identifier to grant token for; e.g. "QmemJQrK7PTQvD3n8gmo9JhyaByyLmETiNR1Y8wS7hv4sP"
    35  	GranteeProfileID string `json:"granteeProfileID"`
    36  	// lifespan of token in nanoseconds; e.g. 2000000000000
    37  	TTL time.Duration `json:"ttl"`
    38  }
    39  
    40  // SetNonZeroDefaults uses default token time-to-live if one isn't set
    41  func (p *CreateAuthTokenParams) SetNonZeroDefaults() {
    42  	if p.TTL == 0 {
    43  		p.TTL = token.DefaultTokenTTL
    44  	}
    45  }
    46  
    47  // Validate returns an error if input params are invalid
    48  func (p *CreateAuthTokenParams) Validate() error {
    49  	if p.GranteeUsername == "" && p.GranteeProfileID == "" {
    50  		return fmt.Errorf("either grantee username or profile is required")
    51  	}
    52  	return nil
    53  }
    54  
    55  // CreateAuthToken constructs a JWT string token suitable for making OAuth
    56  // requests as the grantee user. Creating an access token requires a stored
    57  // private key for the grantee.
    58  // Callers can provide either granteeUsername OR granteeProfileID
    59  func (m AccessMethods) CreateAuthToken(ctx context.Context, p *CreateAuthTokenParams) (string, error) {
    60  	res, _, err := m.d.Dispatch(ctx, dispatchMethodName(m, "createauthtoken"), p)
    61  	if s, ok := res.(string); ok {
    62  		return s, err
    63  	}
    64  	return "", err
    65  }
    66  
    67  // accessImpl is the backing implementation for AccessMethods
    68  type accessImpl struct{}
    69  
    70  func (accessImpl) CreateAuthToken(scp scope, p *CreateAuthTokenParams) (string, error) {
    71  	var (
    72  		grantee *profile.Profile
    73  		err     error
    74  	)
    75  
    76  	if p.GranteeProfileID != "" {
    77  		id, err := profile.IDB58Decode(p.GranteeProfileID)
    78  		if err != nil {
    79  			return "", err
    80  		}
    81  		if grantee, err = scp.Profiles().GetProfile(scp.Context(), id); err != nil {
    82  			return "", err
    83  		}
    84  	} else if p.GranteeUsername == "me" {
    85  		grantee = scp.ActiveProfile()
    86  	} else {
    87  		if grantee, err = profile.ResolveUsername(scp.Context(), scp.Profiles(), p.GranteeUsername); err != nil {
    88  			return "", err
    89  		}
    90  	}
    91  
    92  	pk := grantee.PrivKey
    93  	if pk == nil {
    94  		return "", fmt.Errorf("cannot create token for %q (id: %s), private key is required", grantee.Peername, grantee.ID.Encode())
    95  	}
    96  
    97  	return token.NewPrivKeyAuthToken(pk, grantee.ID.Encode(), p.TTL)
    98  }