2018-07-05 07:13:05 +03:00
// Copyright 2018 The Gitea Authors. All rights reserved.
2022-11-27 21:20:29 +03:00
// SPDX-License-Identifier: MIT
2018-07-05 07:13:05 +03:00
package recaptcha
import (
2020-10-03 06:37:53 +03:00
"context"
2018-07-05 07:13:05 +03:00
"fmt"
2021-09-22 08:38:34 +03:00
"io"
2018-07-05 07:13:05 +03:00
"net/http"
"net/url"
2020-10-03 06:37:53 +03:00
"strings"
2018-07-05 07:13:05 +03:00
2021-07-24 19:03:58 +03:00
"code.gitea.io/gitea/modules/json"
2018-07-05 07:13:05 +03:00
"code.gitea.io/gitea/modules/setting"
2019-05-02 16:09:39 +03:00
"code.gitea.io/gitea/modules/util"
2018-07-05 07:13:05 +03:00
)
// Response is the structure of JSON returned from API
type Response struct {
2020-10-03 06:37:53 +03:00
Success bool ` json:"success" `
ChallengeTS string ` json:"challenge_ts" `
Hostname string ` json:"hostname" `
ErrorCodes [ ] ErrorCode ` json:"error-codes" `
2018-07-05 07:13:05 +03:00
}
2019-11-22 11:03:45 +03:00
const apiURL = "api/siteverify"
2018-07-05 07:13:05 +03:00
// Verify calls Google Recaptcha API to verify token
2020-10-03 06:37:53 +03: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 {
2022-10-24 22:29:17 +03:00
return false , fmt . Errorf ( "Failed to create CAPTCHA request: %w" , err )
2020-10-03 06:37:53 +03:00
}
req . Header . Set ( "Content-Type" , "application/x-www-form-urlencoded" )
resp , err := http . DefaultClient . Do ( req )
2018-07-05 07:13:05 +03:00
if err != nil {
return false , fmt . Errorf ( "Failed to send CAPTCHA response: %s" , err )
}
defer resp . Body . Close ( )
2021-09-22 08:38:34 +03:00
body , err := io . ReadAll ( resp . Body )
2018-07-05 07:13:05 +03:00
if err != nil {
return false , fmt . Errorf ( "Failed to read CAPTCHA response: %s" , err )
}
2021-07-24 19:03:58 +03:00
2018-07-05 07:13:05 +03: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-03 06:37:53 +03: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 07:13:05 +03:00
2020-10-03 06:37:53 +03:00
// Error fulfills the error interface
func ( e ErrorCode ) Error ( ) string {
return e . String ( )
2018-07-05 07:13:05 +03:00
}