1
0
mirror of https://github.com/bingohuang/docker-labs.git synced 2025-07-15 02:37:27 +08:00

Validates that user is a human.

Add google recaptcha as an initial page before creating any session.
To configure recaptcha there are 2 environment variables that are needed
`GOOGLE_RECAPTCHA_SITE_KEY` and `GOOGLE_RECAPTCHA_SITE_SECRET`.
The code contains development defaults that should be set in production
to real values.
**NOTICE: Development defaults assume that the domain is `localhost`**
This commit is contained in:
Jonathan Leibiusky @xetorthio 2016-11-15 16:53:44 -03:00
parent 770945ab86
commit af9986c0f8
7 changed files with 140 additions and 1 deletions

12
api.go
View File

@ -7,12 +7,18 @@ import (
"github.com/franela/play-with-docker/handlers"
"github.com/franela/play-with-docker/services"
"github.com/franela/play-with-docker/templates"
"github.com/gorilla/mux"
"github.com/urfave/negroni"
)
func main() {
welcome, tmplErr := templates.GetWelcomeTemplate()
if tmplErr != nil {
log.Fatal(tmplErr)
}
server := services.CreateWSServer()
server.On("connection", handlers.WS)
@ -27,7 +33,11 @@ func main() {
r.StrictSlash(false)
r.HandleFunc("/ping", http.HandlerFunc(handlers.Ping)).Methods("GET")
r.HandleFunc("/", http.HandlerFunc(handlers.NewSession)).Methods("GET")
r.HandleFunc("/", http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
rw.Write(welcome)
})).Methods("GET")
r.HandleFunc("/", http.HandlerFunc(handlers.NewSession)).Methods("POST")
r.HandleFunc("/sessions/{sessionId}", http.HandlerFunc(handlers.GetSession)).Methods("GET")
r.HandleFunc("/sessions/{sessionId}/instances", http.HandlerFunc(handlers.NewInstance)).Methods("POST")
r.HandleFunc("/sessions/{sessionId}/instances/{instanceName}", http.HandlerFunc(handlers.DeleteInstance)).Methods("DELETE")

View File

@ -9,6 +9,13 @@ import (
)
func NewSession(rw http.ResponseWriter, req *http.Request) {
if !services.IsHuman(req) {
// User it not a human
rw.WriteHeader(http.StatusConflict)
rw.Write([]byte("Only humans are allowed!"))
return
}
s, err := services.NewSession()
if err != nil {
log.Println(err)

59
services/recaptcha.go Normal file
View File

@ -0,0 +1,59 @@
package services
import (
"encoding/json"
"log"
"net/http"
"net/url"
"os"
"strings"
)
func GetGoogleRecaptchaSiteKey() string {
key := os.Getenv("GOOGLE_RECAPTCHA_SITE_KEY")
if key == "" {
// This is a development default. The environment variable should always be set in production.
key = "6LeY_QsUAAAAAOlpVw4MhoLEr50h-dM80oz6M2AX"
}
return key
}
func GetGoogleRecaptchaSiteSecret() string {
key := os.Getenv("GOOGLE_RECAPTCHA_SITE_SECRET")
if key == "" {
// This is a development default. The environment variable should always be set in production.
key = "6LeY_QsUAAAAAHIALCtm0GKfk-UhtXoyJKarnRV8"
}
return key
}
type recaptchaResponse struct {
Success bool `json:"success"`
}
func IsHuman(req *http.Request) bool {
req.ParseForm()
challenge := req.Form.Get("g-recaptcha-response")
// Of X-Forwarded-For exists, it means we are behind a loadbalancer and we should use the real IP address of the user
ip := req.Header.Get("X-Forwarded-For")
if ip == "" {
// Use the standard remote IP address of the request
ip = req.RemoteAddr
}
parts := strings.Split(ip, ":")
resp, postErr := http.PostForm("https://www.google.com/recaptcha/api/siteverify", url.Values{"secret": {GetGoogleRecaptchaSiteSecret()}, "response": {challenge}, "remoteip": {parts[0]}})
if postErr != nil {
log.Println(postErr)
// If there is a problem to connect to google, assume the user is a human so we don't block real users because of technical issues
return true
}
var r recaptchaResponse
json.NewDecoder(resp.Body).Decode(&r)
return r.Success
}

21
templates/welcome.go Normal file
View File

@ -0,0 +1,21 @@
package templates
import (
"bytes"
"html/template"
"github.com/franela/play-with-docker/services"
)
func GetWelcomeTemplate() ([]byte, error) {
welcomeTemplate, tplErr := template.New("welcome").ParseFiles("www/welcome.html")
if tplErr != nil {
return nil, tplErr
}
var b bytes.Buffer
tplExecuteErr := welcomeTemplate.ExecuteTemplate(&b, "GOOGLE_RECAPTCHA_SITE_KEY", services.GetGoogleRecaptchaSiteKey())
if tplExecuteErr != nil {
return nil, tplExecuteErr
}
return b.Bytes(), nil
}

BIN
www/assets/large_h.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

View File

@ -27,3 +27,18 @@ md-card-content.terminal {
color: #1da4eb;
text-align: center;
}
.welcome {
background-color: #e7e7e7;
}
.welcome > div {
text-align: center;
}
.g-recaptcha div {
margin-left: auto;
margin-right: auto;
margin-bottom: auto;
margin-top: 50px;
}

27
www/welcome.html Normal file
View File

@ -0,0 +1,27 @@
{{define "GOOGLE_RECAPTCHA_SITE_KEY"}}
<!doctype html>
<html ng-app="DockerPlay" ng-controller="PlayController">
<head>
<title>Docker Playground</title>
<link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/angular_material/1.1.0/angular-material.min.css">
<link rel="stylesheet" href="/assets/style.css" />
<script src='https://www.google.com/recaptcha/api.js'></script>
</head>
<body class="welcome">
<div>
<h1>Welcome!</h1>
<h2>Before starting we need to verify you are a human</h2>
<form id="welcomeForm" method="POST" action="/">
<div class="g-recaptcha" data-callback="iAmHuman" data-sitekey="{{.}}"></div>
</form>
<img src="/assets/large_h.png" />
</div>
<script>
function iAmHuman(resp) {
document.getElementById('welcomeForm').submit();
}
</script>
</body>
</html>
{{end}}