github.com/aavshr/aws-sdk-go@v1.41.3/service/cloudfront/sign/sign_cookie.go (about)

     1  package sign
     2  
     3  import (
     4  	"crypto/rsa"
     5  	"fmt"
     6  	"net/http"
     7  	"strings"
     8  	"time"
     9  )
    10  
    11  const (
    12  	// CookiePolicyName name of the policy cookie
    13  	CookiePolicyName = "CloudFront-Policy"
    14  	// CookieSignatureName name of the signature cookie
    15  	CookieSignatureName = "CloudFront-Signature"
    16  	// CookieKeyIDName name of the signing Key ID cookie
    17  	CookieKeyIDName = "CloudFront-Key-Pair-Id"
    18  )
    19  
    20  // A CookieOptions optional additional options that can be applied to the signed
    21  // cookies.
    22  type CookieOptions struct {
    23  	Path   string
    24  	Domain string
    25  	Secure bool
    26  }
    27  
    28  // apply will integration the options provided into the base cookie options
    29  // a new copy will be returned. The base CookieOption will not be modified.
    30  func (o CookieOptions) apply(opts ...func(*CookieOptions)) CookieOptions {
    31  	if len(opts) == 0 {
    32  		return o
    33  	}
    34  
    35  	for _, opt := range opts {
    36  		opt(&o)
    37  	}
    38  
    39  	return o
    40  }
    41  
    42  // A CookieSigner provides signing utilities to sign Cookies for Amazon CloudFront
    43  // resources. Using a private key and Credential Key Pair key ID the CookieSigner
    44  // only needs to be created once per Credential Key Pair key ID and private key.
    45  //
    46  // More information about signed Cookies and their structure can be found at:
    47  // http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-setting-signed-cookie-custom-policy.html
    48  //
    49  // To sign a Cookie, create a CookieSigner with your private key and credential
    50  // pair key ID. Once you have a CookieSigner instance you can call Sign or
    51  // SignWithPolicy to sign the URLs.
    52  //
    53  // The signer is safe to use concurrently, but the optional cookies options
    54  // are not safe to modify concurrently.
    55  type CookieSigner struct {
    56  	keyID   string
    57  	privKey *rsa.PrivateKey
    58  
    59  	Opts CookieOptions
    60  }
    61  
    62  // NewCookieSigner constructs and returns a new CookieSigner to be used to for
    63  // signing Amazon CloudFront URL resources with.
    64  func NewCookieSigner(keyID string, privKey *rsa.PrivateKey, opts ...func(*CookieOptions)) *CookieSigner {
    65  	signer := &CookieSigner{
    66  		keyID:   keyID,
    67  		privKey: privKey,
    68  		Opts:    CookieOptions{}.apply(opts...),
    69  	}
    70  
    71  	return signer
    72  }
    73  
    74  // Sign returns the cookies needed to allow user agents to make arbetrary
    75  // requests to cloudfront for the resource(s) defined by the policy.
    76  //
    77  // Sign will create a CloudFront policy with only a resource and condition of
    78  // DateLessThan equal to the expires time provided.
    79  //
    80  // The returned slice cookies should all be added to the Client's cookies or
    81  // server's response.
    82  //
    83  // Example:
    84  //    s := sign.NewCookieSigner(keyID, privKey)
    85  //
    86  //    // Get Signed cookies for a resource that will expire in 1 hour
    87  //    cookies, err := s.Sign("*", time.Now().Add(1 * time.Hour))
    88  //    if err != nil {
    89  //        fmt.Println("failed to create signed cookies", err)
    90  //        return
    91  //    }
    92  //
    93  //    // Or get Signed cookies for a resource that will expire in 1 hour
    94  //    // and set path and domain of cookies
    95  //    cookies, err := s.Sign("*", time.Now().Add(1 * time.Hour), func(o *sign.CookieOptions) {
    96  //        o.Path = "/"
    97  //        o.Domain = ".example.com"
    98  //    })
    99  //    if err != nil {
   100  //        fmt.Println("failed to create signed cookies", err)
   101  //        return
   102  //    }
   103  //
   104  //    // Server Response via http.ResponseWriter
   105  //    for _, c := range cookies {
   106  //        http.SetCookie(w, c)
   107  //    }
   108  //
   109  //    // Client request via the cookie jar
   110  //    if client.CookieJar != nil {
   111  //        for _, c := range cookies {
   112  //           client.Cookie(w, c)
   113  //        }
   114  //    }
   115  func (s CookieSigner) Sign(u string, expires time.Time, opts ...func(*CookieOptions)) ([]*http.Cookie, error) {
   116  	scheme, err := cookieURLScheme(u)
   117  	if err != nil {
   118  		return nil, err
   119  	}
   120  
   121  	resource, err := CreateResource(scheme, u)
   122  	if err != nil {
   123  		return nil, err
   124  	}
   125  
   126  	p := NewCannedPolicy(resource, expires)
   127  	return createCookies(p, s.keyID, s.privKey, s.Opts.apply(opts...))
   128  }
   129  
   130  // Returns and validates the URL's scheme.
   131  // http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-setting-signed-cookie-custom-policy.html#private-content-custom-policy-statement-cookies
   132  func cookieURLScheme(u string) (string, error) {
   133  	parts := strings.SplitN(u, "://", 2)
   134  	if len(parts) != 2 {
   135  		return "", fmt.Errorf("invalid cookie URL, missing scheme")
   136  	}
   137  
   138  	scheme := strings.ToLower(parts[0])
   139  	if scheme != "http" && scheme != "https" && scheme != "http*" {
   140  		return "", fmt.Errorf("invalid cookie URL scheme. Expect http, https, or http*. Go, %s", scheme)
   141  	}
   142  
   143  	return scheme, nil
   144  }
   145  
   146  // SignWithPolicy returns the cookies needed to allow user agents to make
   147  // arbetrairy requets to cloudfront for the resource(s) defined by the policy.
   148  //
   149  // The returned slice cookies should all be added to the Client's cookies or
   150  // server's response.
   151  //
   152  // Example:
   153  //    s := sign.NewCookieSigner(keyID, privKey)
   154  //
   155  //    policy := &sign.Policy{
   156  //        Statements: []sign.Statement{
   157  //            {
   158  //                // Read the provided documentation on how to set this
   159  //                // correctly, you'll probably want to use wildcards.
   160  //                Resource: rawCloudFrontURL,
   161  //                Condition: sign.Condition{
   162  //                    // Optional IP source address range
   163  //                    IPAddress: &sign.IPAddress{SourceIP: "192.0.2.0/24"},
   164  //                    // Optional date URL is not valid until
   165  //                    DateGreaterThan: &sign.AWSEpochTime{time.Now().Add(30 * time.Minute)},
   166  //                    // Required date the URL will expire after
   167  //                    DateLessThan: &sign.AWSEpochTime{time.Now().Add(1 * time.Hour)},
   168  //                },
   169  //            },
   170  //        },
   171  //    }
   172  //
   173  //    // Get Signed cookies for a resource that will expire in 1 hour
   174  //    cookies, err := s.SignWithPolicy(policy)
   175  //    if err != nil {
   176  //        fmt.Println("failed to create signed cookies", err)
   177  //        return
   178  //    }
   179  //
   180  //    // Or get Signed cookies for a resource that will expire in 1 hour
   181  //    // and set path and domain of cookies
   182  //    cookies, err := s.SignWithPolicy(policy, func(o *sign.CookieOptions) {
   183  //        o.Path = "/"
   184  //        o.Domain = ".example.com"
   185  //    })
   186  //    if err != nil {
   187  //        fmt.Println("failed to create signed cookies", err)
   188  //        return
   189  //    }
   190  //
   191  //    // Server Response via http.ResponseWriter
   192  //    for _, c := range cookies {
   193  //        http.SetCookie(w, c)
   194  //    }
   195  //
   196  //    // Client request via the cookie jar
   197  //    if client.CookieJar != nil {
   198  //        for _, c := range cookies {
   199  //           client.Cookie(w, c)
   200  //        }
   201  //    }
   202  func (s CookieSigner) SignWithPolicy(p *Policy, opts ...func(*CookieOptions)) ([]*http.Cookie, error) {
   203  	return createCookies(p, s.keyID, s.privKey, s.Opts.apply(opts...))
   204  }
   205  
   206  // Prepares the cookies to be attached to the header. An (optional) options
   207  // struct is provided in case people don't want to manually edit their cookies.
   208  func createCookies(p *Policy, keyID string, privKey *rsa.PrivateKey, opt CookieOptions) ([]*http.Cookie, error) {
   209  	b64Sig, b64Policy, err := p.Sign(privKey)
   210  	if err != nil {
   211  		return nil, err
   212  	}
   213  
   214  	// Creates proper cookies
   215  	cPolicy := &http.Cookie{
   216  		Name:     CookiePolicyName,
   217  		Value:    string(b64Policy),
   218  		HttpOnly: true,
   219  	}
   220  	cSignature := &http.Cookie{
   221  		Name:     CookieSignatureName,
   222  		Value:    string(b64Sig),
   223  		HttpOnly: true,
   224  	}
   225  	cKey := &http.Cookie{
   226  		Name:     CookieKeyIDName,
   227  		Value:    keyID,
   228  		HttpOnly: true,
   229  	}
   230  
   231  	cookies := []*http.Cookie{cPolicy, cSignature, cKey}
   232  
   233  	// Applie the cookie options
   234  	for _, c := range cookies {
   235  		c.Path = opt.Path
   236  		c.Domain = opt.Domain
   237  		c.Secure = opt.Secure
   238  	}
   239  
   240  	return cookies, nil
   241  }