-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
recaptcha.go
123 lines (101 loc) · 3.06 KB
/
recaptcha.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
package captcha
import (
"bytes"
"encoding/json"
"errors"
"io"
"net/http"
"net/url"
)
var (
ErrRecaptchaMissingSecret = errors.New("secret is missing")
ErrRecaptchaInvalidSecret = errors.New("secret is invalid")
ErrRecaptchaMissingResponse = errors.New("response is missing")
ErrRecaptchaInvalidResponse = errors.New("response is invalid")
ErrRecaptchaBadRequest = errors.New("request is invalid")
ErrRecaptchaTimeout = errors.New("request timed out")
)
type reCaptchaClient struct {
client *http.Client
privateKey string
}
func (c *reCaptchaClient) CheckRequest(r *http.Request) (*Response, error) {
if err := r.ParseForm(); err != nil {
return nil, err
}
return c.CheckPost(r.PostForm.Get("g-recaptcha-response"), r.RemoteAddr)
}
func (c *reCaptchaClient) CheckPost(post string, ip string) (ret *Response, err error) {
// Build request
form := url.Values{}
form.Add("secret", c.privateKey)
form.Add("response", post)
form.Add("remoteip", ip)
req, err := http.NewRequest("POST", "https://www.google.com/recaptcha/api/siteverify", bytes.NewBufferString(form.Encode()))
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", "application/x-www-form-urlencoded; param=value")
// Make request
if c.client == nil {
c.client = http.DefaultClient
}
resp, err := c.client.Do(req)
if err != nil {
return nil, err
}
//goland:noinspection GoUnhandledErrorResult
defer resp.Body.Close()
// Read response
b, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
var recaptchaResponse recaptchaResponse
err = json.Unmarshal(b, &recaptchaResponse)
if err != nil {
return nil, err
}
// Build new response
ret = &Response{
Time: recaptchaResponse.ChallengeTS,
HostName: recaptchaResponse.Hostname,
Errors: nil,
Success: recaptchaResponse.Success,
Score: recaptchaResponse.Score,
Action: recaptchaResponse.Action,
}
if len(recaptchaResponse.ErrorCodes) > 0 {
var errorMap = map[string]error{
"missing-input-secret": ErrRecaptchaMissingSecret,
"invalid-input-secret": ErrRecaptchaInvalidSecret,
"missing-input-response": ErrRecaptchaMissingResponse,
"invalid-input-response": ErrRecaptchaInvalidResponse,
"bad-request": ErrRecaptchaBadRequest,
"timeout-or-duplicate": ErrRecaptchaTimeout,
}
for _, errorCode := range recaptchaResponse.ErrorCodes {
if err, ok := errorMap[errorCode]; ok {
ret.Errors = append(ret.Errors, err)
} else {
ret.Errors = append(ret.Errors, errors.New(errorCode))
}
}
}
return ret, nil
}
func (c *reCaptchaClient) SetClient(client *http.Client) {
c.client = client
}
//goland:noinspection GoUnusedParameter
func (c *reCaptchaClient) setKeys(private, public string) {
c.privateKey = private
}
type recaptchaResponse struct {
Success bool `json:"success"` //
ChallengeTS string `json:"challenge_ts"` //
Hostname string `json:"hostname"` //
ErrorCodes []string `json:"error-codes"` //
Score float32 `json:"score"` // V3
Action string `json:"action"` // V3
}