aboutsummaryrefslogtreecommitdiffstats
path: root/cmd/faucet/faucet.go
diff options
context:
space:
mode:
Diffstat (limited to 'cmd/faucet/faucet.go')
-rw-r--r--cmd/faucet/faucet.go54
1 files changed, 49 insertions, 5 deletions
diff --git a/cmd/faucet/faucet.go b/cmd/faucet/faucet.go
index fd34cdec1..f87cfdb72 100644
--- a/cmd/faucet/faucet.go
+++ b/cmd/faucet/faucet.go
@@ -29,6 +29,7 @@ import (
"io/ioutil"
"math/big"
"net/http"
+ "net/url"
"os"
"path/filepath"
"strings"
@@ -73,6 +74,9 @@ var (
githubUser = flag.String("github.user", "", "GitHub user to authenticate with for Gist access")
githubToken = flag.String("github.token", "", "GitHub personal token to access Gists with")
+ captchaToken = flag.String("captcha.token", "", "Recaptcha site key to authenticate client side")
+ captchaSecret = flag.String("captcha.secret", "", "Recaptcha secret key to authenticate server side")
+
logFlag = flag.Int("loglevel", 3, "Log level to use for Ethereum and the faucet")
)
@@ -96,9 +100,10 @@ func main() {
}
website := new(bytes.Buffer)
template.Must(template.New("").Parse(string(tmpl))).Execute(website, map[string]interface{}{
- "Network": *netnameFlag,
- "Amount": *payoutFlag,
- "Period": period,
+ "Network": *netnameFlag,
+ "Amount": *payoutFlag,
+ "Period": period,
+ "Recaptcha": *captchaToken,
})
// Load and parse the genesis block requested by the user
blob, err := ioutil.ReadFile(*genesisFlag)
@@ -297,7 +302,8 @@ func (f *faucet) apiHandler(conn *websocket.Conn) {
for {
// Fetch the next funding request and validate against github
var msg struct {
- URL string `json:"url"`
+ URL string `json:"url"`
+ Captcha string `json:"captcha"`
}
if err := websocket.JSON.Receive(conn, &msg); err != nil {
return
@@ -308,6 +314,33 @@ func (f *faucet) apiHandler(conn *websocket.Conn) {
}
log.Info("Faucet funds requested", "gist", msg.URL)
+ // If captcha verifications are enabled, make sure we're not dealing with a robot
+ if *captchaToken != "" {
+ form := url.Values{}
+ form.Add("secret", *captchaSecret)
+ form.Add("response", msg.Captcha)
+
+ res, err := http.PostForm("https://www.google.com/recaptcha/api/siteverify", form)
+ if err != nil {
+ websocket.JSON.Send(conn, map[string]string{"error": err.Error()})
+ continue
+ }
+ var result struct {
+ Success bool `json:"success"`
+ Errors json.RawMessage `json:"error-codes"`
+ }
+ err = json.NewDecoder(res.Body).Decode(&result)
+ res.Body.Close()
+ if err != nil {
+ websocket.JSON.Send(conn, map[string]string{"error": err.Error()})
+ continue
+ }
+ if !result.Success {
+ log.Warn("Captcha verification failed", "err", string(result.Errors))
+ websocket.JSON.Send(conn, map[string]string{"error": "Beep-boop, you're a robot!"})
+ continue
+ }
+ }
// Retrieve the gist from the GitHub Gist APIs
parts := strings.Split(msg.URL, "/")
req, _ := http.NewRequest("GET", "https://api.github.com/gists/"+parts[len(parts)-1], nil)
@@ -334,7 +367,7 @@ func (f *faucet) apiHandler(conn *websocket.Conn) {
continue
}
if gist.Owner.Login == "" {
- websocket.JSON.Send(conn, map[string]string{"error": "Nice try ;)"})
+ websocket.JSON.Send(conn, map[string]string{"error": "Anonymous Gists not allowed"})
continue
}
// Iterate over all the files and look for Ethereum addresses
@@ -348,6 +381,17 @@ func (f *faucet) apiHandler(conn *websocket.Conn) {
websocket.JSON.Send(conn, map[string]string{"error": "No Ethereum address found to fund"})
continue
}
+ // Validate the user's existence since the API is unhelpful here
+ if res, err = http.Head("https://github.com/" + gist.Owner.Login); err != nil {
+ websocket.JSON.Send(conn, map[string]string{"error": err.Error()})
+ continue
+ }
+ res.Body.Close()
+
+ if res.StatusCode != 200 {
+ websocket.JSON.Send(conn, map[string]string{"error": "Invalid user... boom!"})
+ continue
+ }
// Ensure the user didn't request funds too recently
f.lock.Lock()
var (