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 }