mirror of
https://github.com/bingohuang/docker-labs.git
synced 2025-07-16 03:07:26 +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:
parent
770945ab86
commit
af9986c0f8
12
api.go
12
api.go
@ -7,12 +7,18 @@ import (
|
|||||||
|
|
||||||
"github.com/franela/play-with-docker/handlers"
|
"github.com/franela/play-with-docker/handlers"
|
||||||
"github.com/franela/play-with-docker/services"
|
"github.com/franela/play-with-docker/services"
|
||||||
|
"github.com/franela/play-with-docker/templates"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"github.com/urfave/negroni"
|
"github.com/urfave/negroni"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
||||||
|
welcome, tmplErr := templates.GetWelcomeTemplate()
|
||||||
|
if tmplErr != nil {
|
||||||
|
log.Fatal(tmplErr)
|
||||||
|
}
|
||||||
|
|
||||||
server := services.CreateWSServer()
|
server := services.CreateWSServer()
|
||||||
|
|
||||||
server.On("connection", handlers.WS)
|
server.On("connection", handlers.WS)
|
||||||
@ -27,7 +33,11 @@ func main() {
|
|||||||
r.StrictSlash(false)
|
r.StrictSlash(false)
|
||||||
|
|
||||||
r.HandleFunc("/ping", http.HandlerFunc(handlers.Ping)).Methods("GET")
|
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}", http.HandlerFunc(handlers.GetSession)).Methods("GET")
|
||||||
r.HandleFunc("/sessions/{sessionId}/instances", http.HandlerFunc(handlers.NewInstance)).Methods("POST")
|
r.HandleFunc("/sessions/{sessionId}/instances", http.HandlerFunc(handlers.NewInstance)).Methods("POST")
|
||||||
r.HandleFunc("/sessions/{sessionId}/instances/{instanceName}", http.HandlerFunc(handlers.DeleteInstance)).Methods("DELETE")
|
r.HandleFunc("/sessions/{sessionId}/instances/{instanceName}", http.HandlerFunc(handlers.DeleteInstance)).Methods("DELETE")
|
||||||
|
@ -9,6 +9,13 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func NewSession(rw http.ResponseWriter, req *http.Request) {
|
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()
|
s, err := services.NewSession()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
log.Println(err)
|
||||||
|
59
services/recaptcha.go
Normal file
59
services/recaptcha.go
Normal 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
21
templates/welcome.go
Normal 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
BIN
www/assets/large_h.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 35 KiB |
@ -27,3 +27,18 @@ md-card-content.terminal {
|
|||||||
color: #1da4eb;
|
color: #1da4eb;
|
||||||
text-align: center;
|
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
27
www/welcome.html
Normal 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}}
|
Loading…
x
Reference in New Issue
Block a user