cmd/faucet: add optional recaptcha validation support
This commit is contained in:
parent
cb3f5f8b93
commit
03dffe3efd
@ -29,6 +29,7 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"math/big"
|
"math/big"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
@ -73,6 +74,9 @@ var (
|
|||||||
githubUser = flag.String("github.user", "", "GitHub user to authenticate with for Gist access")
|
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")
|
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")
|
logFlag = flag.Int("loglevel", 3, "Log level to use for Ethereum and the faucet")
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -96,9 +100,10 @@ func main() {
|
|||||||
}
|
}
|
||||||
website := new(bytes.Buffer)
|
website := new(bytes.Buffer)
|
||||||
template.Must(template.New("").Parse(string(tmpl))).Execute(website, map[string]interface{}{
|
template.Must(template.New("").Parse(string(tmpl))).Execute(website, map[string]interface{}{
|
||||||
"Network": *netnameFlag,
|
"Network": *netnameFlag,
|
||||||
"Amount": *payoutFlag,
|
"Amount": *payoutFlag,
|
||||||
"Period": period,
|
"Period": period,
|
||||||
|
"Recaptcha": *captchaToken,
|
||||||
})
|
})
|
||||||
// Load and parse the genesis block requested by the user
|
// Load and parse the genesis block requested by the user
|
||||||
blob, err := ioutil.ReadFile(*genesisFlag)
|
blob, err := ioutil.ReadFile(*genesisFlag)
|
||||||
@ -297,7 +302,8 @@ func (f *faucet) apiHandler(conn *websocket.Conn) {
|
|||||||
for {
|
for {
|
||||||
// Fetch the next funding request and validate against github
|
// Fetch the next funding request and validate against github
|
||||||
var msg struct {
|
var msg struct {
|
||||||
URL string `json:"url"`
|
URL string `json:"url"`
|
||||||
|
Captcha string `json:"captcha"`
|
||||||
}
|
}
|
||||||
if err := websocket.JSON.Receive(conn, &msg); err != nil {
|
if err := websocket.JSON.Receive(conn, &msg); err != nil {
|
||||||
return
|
return
|
||||||
@ -306,8 +312,35 @@ func (f *faucet) apiHandler(conn *websocket.Conn) {
|
|||||||
websocket.JSON.Send(conn, map[string]string{"error": "URL doesn't link to GitHub Gists"})
|
websocket.JSON.Send(conn, map[string]string{"error": "URL doesn't link to GitHub Gists"})
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
log.Info("Faucet funds requested", "addr", conn.RemoteAddr(), "gist", msg.URL)
|
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
|
// Retrieve the gist from the GitHub Gist APIs
|
||||||
parts := strings.Split(msg.URL, "/")
|
parts := strings.Split(msg.URL, "/")
|
||||||
req, _ := http.NewRequest("GET", "https://api.github.com/gists/"+parts[len(parts)-1], nil)
|
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
|
continue
|
||||||
}
|
}
|
||||||
if gist.Owner.Login == "" {
|
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
|
continue
|
||||||
}
|
}
|
||||||
// Iterate over all the files and look for Ethereum addresses
|
// Iterate over all the files and look for Ethereum addresses
|
||||||
@ -349,7 +382,7 @@ func (f *faucet) apiHandler(conn *websocket.Conn) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// Validate the user's existence since the API is unhelpful here
|
// Validate the user's existence since the API is unhelpful here
|
||||||
if res, err = http.Head("https://github.com/%s", gist.Owner.Login); err != nil {
|
if res, err = http.Head("https://github.com/" + gist.Owner.Login); err != nil {
|
||||||
websocket.JSON.Send(conn, map[string]string{"error": err.Error()})
|
websocket.JSON.Send(conn, map[string]string{"error": err.Error()})
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -51,9 +51,10 @@
|
|||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<input id="gist" type="text" class="form-control" placeholder="GitHub Gist URL containing your Ethereum address...">
|
<input id="gist" type="text" class="form-control" placeholder="GitHub Gist URL containing your Ethereum address...">
|
||||||
<span class="input-group-btn">
|
<span class="input-group-btn">
|
||||||
<button class="btn btn-default" type="button" onclick="submit()">Give me Ether!</button>
|
<button class="btn btn-default" type="button" onclick="{{if .Recaptcha}}grecaptcha.execute(){{else}}submit(){{end}}">Give me Ether!</button>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>{{if .Recaptcha}}
|
||||||
|
<div class="g-recaptcha" data-sitekey="{{.Recaptcha}}" data-callback="submit" data-size="invisible"></div>{{end}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row" style="margin-top: 32px;">
|
<div class="row" style="margin-top: 32px;">
|
||||||
@ -89,8 +90,9 @@
|
|||||||
var server;
|
var server;
|
||||||
|
|
||||||
// Define the function that submits a gist url to the server
|
// Define the function that submits a gist url to the server
|
||||||
var submit = function() {
|
var submit = function({{if .Recaptcha}}captcha{{end}}) {
|
||||||
server.send(JSON.stringify({url: $("#gist")[0].value}));
|
server.send(JSON.stringify({url: $("#gist")[0].value{{if .Recaptcha}}, captcha: captcha{{end}}}));{{if .Recaptcha}}
|
||||||
|
grecaptcha.reset();{{end}}
|
||||||
};
|
};
|
||||||
// Define a method to reconnect upon server loss
|
// Define a method to reconnect upon server loss
|
||||||
var reconnect = function() {
|
var reconnect = function() {
|
||||||
@ -138,6 +140,7 @@
|
|||||||
}
|
}
|
||||||
// Establish a websocket connection to the API server
|
// Establish a websocket connection to the API server
|
||||||
reconnect();
|
reconnect();
|
||||||
</script>
|
</script>{{if .Recaptcha}}
|
||||||
|
<script src="https://www.google.com/recaptcha/api.js" async defer></script>{{end}}
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user