Albirew/nyaa-pantsu
Archivé
1
0
Bifurcation 0

Merge pull request #127 from bakape/captchas

Captchas
Cette révision appartient à :
Eliot Whalan 2017-05-07 19:27:57 +10:00 révisé par GitHub
révision 83e496a224
11 fichiers modifiés avec 183 ajouts et 34 suppressions

Voir le fichier

@ -13,6 +13,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 get github.com/microcosm-cc/bluemonday
- go get github.com/tcnksm/ghr
- go get github.com/axw/gocov/gocov

Voir le fichier

@ -106,6 +106,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
@ -32,6 +33,7 @@ func init() {
Router.HandleFunc("/user/register", UserRegisterFormHandler).Name("user_register").Methods("GET")
Router.HandleFunc("/user/login", UserLoginFormHandler).Name("user_login").Methods("GET")
Router.HandleFunc("/user/register", UserRegisterPostHandler).Name("user_register").Methods("POST")
Router.PathPrefix("/captcha").Methods("GET").HandlerFunc(captcha.ServeFiles)
Router.NotFoundHandler = http.HandlerFunc(NotFoundHandler)
}

Voir le fichier

@ -3,14 +3,16 @@ package router
import (
"encoding/hex"
"errors"
"github.com/ewhal/nyaa/util"
"github.com/ewhal/nyaa/util/metainfo"
"github.com/microcosm-cc/bluemonday"
"github.com/zeebo/bencode"
"net/http"
"net/url"
"strconv"
"strings"
"github.com/ewhal/nyaa/service/captcha"
"github.com/ewhal/nyaa/util"
"github.com/ewhal/nyaa/util/metainfo"
"github.com/microcosm-cc/bluemonday"
"github.com/zeebo/bencode"
)
// UploadForm serializing HTTP form for torrent upload
@ -22,6 +24,7 @@ type UploadForm struct {
CategoryId int
SubCategoryId int
Description string
captcha.Captcha
}
// TODO: these should be in another package (?)
@ -64,6 +67,7 @@ func (f *UploadForm) ExtractInfo(r *http.Request) error {
f.Category = r.FormValue(UploadFormCategory)
f.Description = r.FormValue(UploadFormDescription)
f.Magnet = r.FormValue(UploadFormMagnet)
f.Captcha = captcha.Extract(r)
// trim whitespaces
f.Name = util.TrimWhitespaces(f.Name)
@ -76,6 +80,7 @@ func (f *UploadForm) ExtractInfo(r *http.Request) error {
if len(f.Description) == 0 {
return ErrInvalidTorrentDescription
}
catsSplit := strings.Split(f.Category, "_")

Voir le fichier

@ -2,13 +2,15 @@ package router
import (
"fmt"
"github.com/ewhal/nyaa/db"
"github.com/ewhal/nyaa/model"
"github.com/gorilla/mux"
"html/template"
"net/http"
"strconv"
"time"
"github.com/ewhal/nyaa/db"
"github.com/ewhal/nyaa/model"
"github.com/ewhal/nyaa/service/captcha"
"github.com/gorilla/mux"
)
var uploadTemplate = template.Must(template.New("upload").Funcs(FuncMap).ParseFiles("templates/index.html", "templates/upload.html"))
@ -24,6 +26,12 @@ func UploadHandler(w http.ResponseWriter, r *http.Request) {
defer r.Body.Close()
err = uploadForm.ExtractInfo(r)
if err == nil {
if !captcha.Authenticate(uploadForm.Captcha) {
// TODO: Prettier passing of mistyoed captcha errors
http.Error(w, captcha.ErrInvalidCaptcha.Error(), 403)
return
}
//validate name + hash
//add to db and redirect depending on result
torrent := model.Torrents{
@ -45,6 +53,7 @@ func UploadHandler(w http.ResponseWriter, r *http.Request) {
}
fmt.Printf("%+v\n", uploadForm)
} else if r.Method == "GET" {
uploadForm.CaptchaID = captcha.GetID(r.RemoteAddr)
htv := UploadTemplateVariables{uploadForm, NewSearchForm(), Navigation{}, r.URL, mux.CurrentRoute(r)}
err = uploadTemplate.ExecuteTemplate(w, "index.html", htv)
} else {

Voir le fichier

@ -4,16 +4,17 @@ import (
"html/template"
"net/http"
"github.com/ewhal/nyaa/service/user/form"
"github.com/ewhal/nyaa/service/user"
"github.com/ewhal/nyaa/util/modelHelper"
"github.com/ewhal/nyaa/service/user/form"
"github.com/ewhal/nyaa/util/languages"
"github.com/ewhal/nyaa/util/modelHelper"
"github.com/gorilla/mux"
)
var viewRegisterTemplate = template.Must(template.New("userRegister").Funcs(FuncMap).ParseFiles("templates/index.html", "templates/user/register.html"))
var viewLoginTemplate = template.Must(template.New("userLogin").Funcs(FuncMap).ParseFiles("templates/index.html", "templates/user/login.html"))
var viewRegisterSuccessTemplate = template.Must(template.New("userRegisterSuccess").Funcs(FuncMap).ParseFiles("templates/index.html", "templates/user/signup_success.html"))
//var viewTemplate = template.Must(template.New("view").Funcs(FuncMap).ParseFiles("templates/index.html", "templates/view.html"))
//var viewTemplate = template.Must(template.New("view").Funcs(FuncMap).ParseFiles("templates/index.html", "templates/view.html"))
@ -24,7 +25,6 @@ func init() {
}
// Getting View User Registration
func UserRegisterFormHandler(w http.ResponseWriter, r *http.Request) {
b := form.RegistrationForm{}
modelHelper.BindValueForm(&b, r)
@ -61,15 +61,15 @@ func UserProfileFormHandler(w http.ResponseWriter, r *http.Request) {
// Post Registration controller
func UserRegisterPostHandler(w http.ResponseWriter, r *http.Request) {
// Check same Password
if ((r.PostFormValue("password") == r.PostFormValue("password_confirm"))&&(r.PostFormValue("password") != "")) {
if ((form.EmailValidation(r.PostFormValue("email")))&&(form.ValidateUsername(r.PostFormValue("username")))) {
_ , err := userService.CreateUser(w, r)
if (err == nil) {
if (r.PostFormValue("password") == r.PostFormValue("password_confirm")) && (r.PostFormValue("password") != "") {
if (form.EmailValidation(r.PostFormValue("email"))) && (form.ValidateUsername(r.PostFormValue("username"))) {
_, err := userService.CreateUser(w, r)
if err == nil {
b := form.RegistrationForm{}
htv := UserRegisterTemplateVariables{b, NewSearchForm(), Navigation{}, r.URL, mux.CurrentRoute(r)}
err = viewRegisterSuccessTemplate.ExecuteTemplate(w, "index.html", htv)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
http.Error(w, err.Error(), http.StatusInternalServerError)
}
} else {
UserRegisterFormHandler(w, r)

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

@ -0,0 +1,108 @@
package captcha
import (
"errors"
"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),
}
ErrInvalidCaptcha = errors.New("invalid captcha")
)
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)
}
// Extract a Captcha struct from an HTML form
func Extract(r *http.Request) Captcha {
return Captcha{
CaptchaID: r.FormValue("captchaID"),
Solution: r.FormValue("solution"),
}
}
// 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"
)
@ -18,30 +19,35 @@ func EmailValidation(email string) bool {
return false
}
func ValidateUsername(username string) bool {
exp, err := regexp.Compile(USERNAME_REGEX)
exp, err := regexp.Compile(USERNAME_REGEX)
if (username == "") {
return false;
}
if ((len(username) < 3) || (len(username) > 15)) {
return false;
}
if regexpCompiled := log.CheckError(err); regexpCompiled {
if username == "" {
return false
}
if (len(username) < 3) || (len(username) > 15) {
return false
}
if regexpCompiled := log.CheckError(err); regexpCompiled {
if exp.MatchString(username) {
return false
}
} else {
return false
}
return true
return true
}
// RegistrationForm is used when creating a user.
type RegistrationForm struct {
Username string `form:"username" binding:"required"`
Email string `form:"email" binding:"required"`
Password string `form:"password" binding:"required"`
Username string `form:"registrationUsername" binding:"required"`
Email string `form:"registrationEmail" binding:"required"`
Password string `form:"registrationPassword" binding:"required"`
Username string `form:"username" binding:"required"`
Email string `form:"email" binding:"required"`
Password string `form:"password" 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}}">
style="width:60rem" placeholder="Magnet Link" value="{{.Magnet}}">
</div>
<div class="form-group">
<label for="c">Category</label>
@ -47,6 +47,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

@ -37,7 +37,9 @@
{{T "terms_conditions_confirm" }}
</div>
</div>
{{block "captcha" .}}{{end}}
<hr class="colorgraph">
<div class="row">
<div class="col-xs-12 col-md-6"><input type="submit" value="{{T "register" }}" class="btn btn-primary btn-block btn-lg" tabindex="7"></div>
@ -66,4 +68,4 @@
</div><!-- /.modal -->
</div>
{{end}}
{{define "js_footer"}}<script type="text/javascript" charset="utf-8" src="{{.URL.Parse "/js/registerPage.js"}}"></script>{{end}}
{{define "js_footer"}}<script type="text/javascript" charset="utf-8" src="{{.URL.Parse "/js/registerPage.js"}}"></script>{{end}}