2018-07-05 00:13:05 -04:00
// Copyright 2018 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package recaptcha
import (
2020-10-02 22:37:53 -05:00
"context"
2018-07-05 00:13:05 -04:00
"fmt"
2021-09-22 13:38:34 +08:00
"io"
2018-07-05 00:13:05 -04:00
"net/http"
"net/url"
2020-10-02 22:37:53 -05:00
"strings"
2018-07-05 00:13:05 -04:00
2021-07-25 00:03:58 +08:00
"code.gitea.io/gitea/modules/json"
2018-07-05 00:13:05 -04:00
"code.gitea.io/gitea/modules/setting"
2019-05-02 14:09:39 +01:00
"code.gitea.io/gitea/modules/util"
2018-07-05 00:13:05 -04:00
)
// Response is the structure of JSON returned from API
type Response struct {
2020-10-02 22:37:53 -05:00
Success bool ` json:"success" `
ChallengeTS string ` json:"challenge_ts" `
Hostname string ` json:"hostname" `
ErrorCodes [ ] ErrorCode ` json:"error-codes" `
2018-07-05 00:13:05 -04:00
}
2019-11-22 17:03:45 +09:00
const apiURL = "api/siteverify"
2018-07-05 00:13:05 -04:00
// Verify calls Google Recaptcha API to verify token
2020-10-02 22:37:53 -05:00
func Verify ( ctx context . Context , response string ) ( bool , error ) {
post := url . Values {
"secret" : { setting . Service . RecaptchaSecret } ,
"response" : { response } ,
}
// Basically a copy of http.PostForm, but with a context
req , err := http . NewRequestWithContext ( ctx , http . MethodPost ,
util . URLJoin ( setting . Service . RecaptchaURL , apiURL ) , strings . NewReader ( post . Encode ( ) ) )
if err != nil {
return false , fmt . Errorf ( "Failed to create CAPTCHA request: %v" , err )
}
req . Header . Set ( "Content-Type" , "application/x-www-form-urlencoded" )
resp , err := http . DefaultClient . Do ( req )
2018-07-05 00:13:05 -04:00
if err != nil {
return false , fmt . Errorf ( "Failed to send CAPTCHA response: %s" , err )
}
defer resp . Body . Close ( )
2021-09-22 13:38:34 +08:00
body , err := io . ReadAll ( resp . Body )
2018-07-05 00:13:05 -04:00
if err != nil {
return false , fmt . Errorf ( "Failed to read CAPTCHA response: %s" , err )
}
2021-07-25 00:03:58 +08:00
2018-07-05 00:13:05 -04:00
var jsonResponse Response
err = json . Unmarshal ( body , & jsonResponse )
if err != nil {
return false , fmt . Errorf ( "Failed to parse CAPTCHA response: %s" , err )
}
2020-10-02 22:37:53 -05:00
var respErr error
if len ( jsonResponse . ErrorCodes ) > 0 {
respErr = jsonResponse . ErrorCodes [ 0 ]
}
return jsonResponse . Success , respErr
}
// ErrorCode is a reCaptcha error
type ErrorCode string
// String fulfills the Stringer interface
func ( e ErrorCode ) String ( ) string {
switch e {
case "missing-input-secret" :
return "The secret parameter is missing."
case "invalid-input-secret" :
return "The secret parameter is invalid or malformed."
case "missing-input-response" :
return "The response parameter is missing."
case "invalid-input-response" :
return "The response parameter is invalid or malformed."
case "bad-request" :
return "The request is invalid or malformed."
case "timeout-or-duplicate" :
return "The response is no longer valid: either is too old or has been used previously."
}
return string ( e )
}
2018-07-05 00:13:05 -04:00
2020-10-02 22:37:53 -05:00
// Error fulfills the error interface
func ( e ErrorCode ) Error ( ) string {
return e . String ( )
2018-07-05 00:13:05 -04:00
}