Albirew/nyaa-pantsu
Archivé
1
0
Bifurcation 0

Internal IP-specific captchas

Cette révision appartient à :
bakape 2017-05-07 11:25:09 +03:00
Parent 2ab9224dfe
révision 2f55e478e0
11 fichiers modifiés avec 159 ajouts et 24 suppressions

Voir le fichier

@ -12,6 +12,7 @@ install:
- go get github.com/gorilla/securecookie
- go get golang.org/x/crypto/bcrypt
- go get github.com/nicksnyder/go-i18n/i18n
- go get github.com/dchest/captcha
- go build
deploy:
provider: releases

Voir le fichier

@ -103,6 +103,12 @@ a {
padding: 4px;
}
.captcha-container {
display: grid;
grid-template-rows: auto;
grid-template-columns: 240px;
}
tr.torrent-info td.date {
white-space: nowrap;
}

Voir le fichier

@ -1,9 +1,10 @@
package router
import (
"github.com/gorilla/mux"
"net/http"
"github.com/ewhal/nyaa/service/captcha"
"github.com/gorilla/mux"
)
var Router *mux.Router
@ -30,4 +31,5 @@ func init() {
Router.HandleFunc("/view/{id}", ViewHandler).Name("view_torrent")
Router.HandleFunc("/upload", UploadHandler).Name("upload")
Router.HandleFunc("/user/register", UserRegisterFormHandler).Name("user_register")
Router.PathPrefix("/captcha").Methods("GET").HandlerFunc(captcha.ServeFiles)
}

Voir le fichier

@ -2,18 +2,16 @@ package router
import (
"errors"
"net/http"
"github.com/ewhal/nyaa/util"
"github.com/ewhal/nyaa/util/metainfo"
"github.com/zeebo/bencode"
"net/http"
)
// UploadForm serializing HTTP form for torrent upload
type UploadForm struct {
Name string
Magnet string
Category string
Description string
Name, Magnet, Category, Description, CaptchaID string
}
// TODO: these should be in another package (?)

Voir le fichier

@ -4,6 +4,7 @@ import (
"html/template"
"net/http"
"github.com/ewhal/nyaa/service/captcha"
"github.com/gorilla/mux"
)
@ -15,18 +16,27 @@ func init() {
func UploadHandler(w http.ResponseWriter, r *http.Request) {
var err error
var uploadForm UploadForm
if r.Method == "POST" {
switch r.Method {
case "POST":
var form UploadForm
defer r.Body.Close()
err = uploadForm.ExtractInfo(r)
err = form.ExtractInfo(r)
if err == nil {
//validate name + hash
//add to db and redirect depending on result
// validate name + hash
// authenticate captcha
// add to db and redirect depending on result
}
case "GET":
htv := UploadTemplateVariables{
Upload: UploadForm{
CaptchaID: captcha.GetID(r.RemoteAddr),
},
Search: NewSearchForm(),
URL: r.URL,
Route: mux.CurrentRoute(r),
}
} else if r.Method == "GET" {
htv := UploadTemplateVariables{uploadForm, NewSearchForm(), Navigation{}, r.URL, mux.CurrentRoute(r)}
err = uploadTemplate.ExecuteTemplate(w, "index.html", htv)
} else {
default:
w.WriteHeader(http.StatusMethodNotAllowed)
return
}

Voir le fichier

@ -4,6 +4,7 @@ import (
"html/template"
"net/http"
"github.com/ewhal/nyaa/service/captcha"
"github.com/ewhal/nyaa/service/user/form"
"github.com/ewhal/nyaa/util/modelHelper"
"github.com/gorilla/mux"
@ -21,10 +22,16 @@ func init() {
// Getting View User Registration
func UserRegisterFormHandler(w http.ResponseWriter, r *http.Request) {
b := form.RegistrationForm{}
b := form.RegistrationForm{
CaptchaID: captcha.GetID(r.RemoteAddr),
}
modelHelper.BindValueForm(b, r)
htv := UserRegisterTemplateVariables{b, NewSearchForm(), Navigation{}, r.URL, mux.CurrentRoute(r)}
htv := UserRegisterTemplateVariables{
RegistrationForm: b,
Search: NewSearchForm(),
URL: r.URL,
Route: mux.CurrentRoute(r),
}
err := viewTemplate.ExecuteTemplate(w, "index.html", htv)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)

97
service/captcha/capthca.go Fichier normal
Voir le fichier

@ -0,0 +1,97 @@
package captcha
import (
"net/http"
"sync"
"time"
"github.com/dchest/captcha"
)
const lifetime = time.Minute * 20
var (
server = captcha.Server(captcha.StdWidth, captcha.StdHeight)
captchas = captchaMap{
m: make(map[string]store, 64),
}
)
func init() {
captcha.SetCustomStore(captcha.NewMemoryStore(1<<10, lifetime))
go func() {
t := time.Tick(time.Minute)
for {
<-t
captchas.cleanUp()
}
}()
}
// Captcha is to be embedded into any form struct requiring a captcha
type Captcha struct {
CaptchaID, Solution string
}
// Captchas are IP-specific and need eventual cleanup
type captchaMap struct {
sync.Mutex
m map[string]store
}
type store struct {
id string
created time.Time
}
// Returns a captcha id by IP. If a captcha for this IP already exists, it is
// reloaded and returned. Otherwise, a new captcha is created.
func (n *captchaMap) get(ip string) string {
n.Lock()
defer n.Unlock()
old, ok := n.m[ip]
// No existing captcha, it expired or this IP already used the captcha
if !ok || !captcha.Reload(old.id) {
id := captcha.New()
n.m[ip] = store{
id: id,
created: time.Now(),
}
return id
}
old.created = time.Now()
n.m[ip] = old
return old.id
}
// Remove expired ip -> captchaID mappings
func (n *captchaMap) cleanUp() {
n.Lock()
defer n.Unlock()
till := time.Now().Add(-lifetime)
for ip, c := range n.m {
if c.created.Before(till) {
delete(n.m, ip)
}
}
}
// GetID returns a new or previous captcha id by IP
func GetID(ip string) string {
return captchas.get(ip)
}
// ServeFiles serves captcha images and audio
func ServeFiles(w http.ResponseWriter, r *http.Request) {
server.ServeHTTP(w, r)
}
// Authenticate check's if a captcha solution is valid
func Authenticate(req Captcha) bool {
return captcha.VerifyString(req.CaptchaID, req.Solution)
}

Voir le fichier

@ -2,6 +2,7 @@ package form
import (
"regexp"
"github.com/ewhal/nyaa/util/log"
)
@ -19,9 +20,10 @@ func EmailValidation(email string) bool {
// RegistrationForm is used when creating a user.
type RegistrationForm struct {
Username string `form:"registrationUsername" binding:"required"`
Email string `form:"registrationEmail" binding:"required"`
Password string `form:"registrationPassword" binding:"required"`
Username string `form:"registrationUsername" binding:"required"`
Email string `form:"registrationEmail" binding:"required"`
Password string `form:"registrationPassword" binding:"required"`
CaptchaID string `form:"captchaID" binding:"required"`
}
// RegistrationForm is used when creating a user authentication.

7
templates/_capthca.html Fichier normal
Voir le fichier

@ -0,0 +1,7 @@
{{define "captcha"}}
<div class="form-group captcha-container">
<input type="text" name="capthcaID" value="{{.CaptchaID}}" hidden>
<img src="/captcha/{{.CaptchaID}}.png">
<input type="number" name="solution" class="form-control" placeholder="Captcha" required>
</div>
{{end}}

Voir le fichier

@ -17,7 +17,7 @@
<div class="form-group">
<label for="Magnet">Magnet Link</label>
<input type="text" name="magnet" class="form-control"
style="width:60rem" placeholder="Magnet Link" value="{{.Magnet}}" required>
style="width:60rem" placeholder="Magnet Link" value="{{.Magnet}}" required>
</div>
<div class="form-group">
<label for="c">Category</label>
@ -53,6 +53,9 @@
<label for="desc">Torrent Description</label>
<textarea name="desc" class="form-control" rows="10">{{.Description}}</textarea>
</div>
{{block "captcha" .}}{{end}}
<button type="submit" class="btn btn-success">Upload</button>
<br />
<br />

Voir le fichier

@ -36,7 +36,9 @@
By clicking <strong class="label label-primary">Register</strong>, you agree to the <a href="#" data-toggle="modal" data-target="#t_and_c_m">Terms and Conditions</a> set out by this site, including our Cookie Use.
</div>
</div>
{{block "captcha" .}}{{end}}
<hr class="colorgraph">
<div class="row">
<div class="col-xs-12 col-md-6"><input type="submit" value="Register" class="btn btn-primary btn-block btn-lg" tabindex="7"></div>
@ -63,4 +65,4 @@
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
</div>
{{end}}
{{end}}