2021-11-04 01:14:51 +00:00
package user
import (
2021-11-22 22:11:37 +00:00
"bytes"
2021-11-04 01:14:51 +00:00
"embed"
2021-11-24 02:27:07 +00:00
"encoding/base64"
2021-11-04 01:14:51 +00:00
"fmt"
2021-11-24 02:27:07 +00:00
"github.com/dchest/captcha"
2021-11-04 01:14:51 +00:00
"github.com/go-chi/chi/v5"
"github.com/go-chi/jwtauth/v5"
2021-11-24 02:27:07 +00:00
"github.com/rs/zerolog/log"
2021-11-04 01:14:51 +00:00
"github.com/unrolled/render"
2021-11-24 02:27:07 +00:00
"net/mail"
"regexp"
2021-11-23 20:32:55 +00:00
2021-11-22 22:11:37 +00:00
gomail "gopkg.in/mail.v2"
"io"
2021-11-04 01:14:51 +00:00
"net/http"
"time"
)
// content holds our static web server content.
//go:embed templates/*
var webserver embed . FS
var ren * render . Render
2021-11-22 22:11:37 +00:00
var uc UserClient
2021-11-04 01:14:51 +00:00
var tokenAuth * jwtauth . JWTAuth
2021-11-22 22:46:01 +00:00
var d * gomail . Dialer
2021-11-23 19:21:43 +00:00
type UserConfig struct {
RegisterMail * gomail . Message
URLValidationToken string
}
var userconfig UserConfig
func NewUserConfig ( ) UserConfig {
uc := UserConfig { }
m := gomail . NewMessage ( )
m . SetHeader ( "From" , "test@keks.cloud" )
m . SetHeader ( "To" , "to@example.com" )
m . SetHeader ( "Subject" , "Activate your Account" )
uc . RegisterMail = m
2021-11-24 02:27:07 +00:00
uc . URLValidationToken = "http://localhost:3000/validate/%s"
2021-11-23 19:21:43 +00:00
return uc
}
2021-11-24 02:27:07 +00:00
func Register ( router chi . Router , template func ( http . Handler ) http . Handler , token * jwtauth . JWTAuth , userClient UserClient , dialer * gomail . Dialer , config UserConfig ) {
2021-11-22 22:11:37 +00:00
uc = userClient
2021-11-23 19:21:43 +00:00
userconfig = config
2021-11-04 01:14:51 +00:00
ren = render . New ( render . Options {
//Layout: "layout",
FileSystem : & render . EmbedFileSystem {
FS : webserver ,
} ,
} )
2021-11-24 02:27:07 +00:00
router . Group ( func ( r chi . Router ) {
r . Use ( template )
r . Get ( "/login" , loginForm )
r . Post ( "/login" , login )
r . Get ( "/register" , registerForm )
r . Post ( "/register" , register )
r . Get ( "/logout" , logout )
} )
router . Get ( "/captcha/{id}.png" , captchaImage )
2021-11-04 01:14:51 +00:00
2021-11-08 01:25:36 +00:00
tokenAuth = token
2021-11-04 01:14:51 +00:00
2021-11-22 22:46:01 +00:00
d = dialer
2021-11-04 01:14:51 +00:00
router . Group ( func ( r chi . Router ) {
r . Use ( jwtauth . Verifier ( tokenAuth ) )
r . Get ( "/me" , func ( w http . ResponseWriter , r * http . Request ) {
_ , claims , _ := jwtauth . FromContext ( r . Context ( ) )
if val , ok := claims [ "username" ] ; ok {
w . Write ( [ ] byte ( fmt . Sprintf ( "hi %v" , val ) ) )
} else {
w . Write ( [ ] byte ( "Du bist nicht eingeloggt" ) )
}
} )
} )
}
2021-11-24 02:27:07 +00:00
func captchaImage ( w http . ResponseWriter , r * http . Request ) {
id := chi . URLParam ( r , "id" )
w . Header ( ) . Set ( "Content-Type" , "image/png" )
captcha . WriteImage ( w , id , 400 , 200 )
}
2021-11-04 01:14:51 +00:00
func loginForm ( w http . ResponseWriter , r * http . Request ) {
2021-11-24 02:27:07 +00:00
ren . HTML ( w , http . StatusOK , "login" , map [ string ] string { "captcha" : captcha . New ( ) } )
2021-11-04 01:14:51 +00:00
}
2021-11-09 18:23:19 +00:00
func registerForm ( w http . ResponseWriter , r * http . Request ) {
2021-11-24 02:27:07 +00:00
ren . HTML ( w , http . StatusOK , "register" , map [ string ] string { "captcha" : captcha . New ( ) } )
2021-11-09 18:23:19 +00:00
}
2021-11-04 01:14:51 +00:00
func login ( w http . ResponseWriter , r * http . Request ) {
r . ParseForm ( )
2021-11-24 02:27:07 +00:00
if captcha . VerifyString ( r . FormValue ( "captchaid" ) , r . FormValue ( "captcha" ) ) == false {
log . Debug ( ) . Str ( "captchaid" , r . FormValue ( "captchaid" ) ) . Str ( "input" , r . FormValue ( "captcha" ) ) . Interface ( "digits" , [ ] byte ( r . FormValue ( "captcha" ) ) ) . Msg ( "Captcha wrong" )
ren . HTML ( w , http . StatusOK , "login" , map [ string ] string { "captcha" : captcha . New ( ) , "msg" : "Captcha code was wrong" , "username" : r . FormValue ( "username" ) } )
return
}
2021-11-04 01:14:51 +00:00
2021-11-24 02:27:07 +00:00
res , err := uc . login ( r . FormValue ( "username" ) , r . FormValue ( "password" ) , true )
2021-11-23 20:32:55 +00:00
if err != nil {
2021-11-24 02:27:07 +00:00
ren . HTML ( w , http . StatusOK , "login" , map [ string ] string { "captcha" : captcha . New ( ) , "msg" : err . Error ( ) , "username" : r . FormValue ( "username" ) } )
2021-11-23 20:32:55 +00:00
return
}
2021-11-04 01:14:51 +00:00
if res {
_ , tokenstring , err := tokenAuth . Encode ( map [ string ] interface { } { "username" : r . FormValue ( "username" ) } )
if err != nil {
panic ( err )
}
expiration := time . Now ( ) . Add ( 365 * 24 * time . Hour )
cookie := http . Cookie { Name : "jwt" , Value : tokenstring , Expires : expiration }
http . SetCookie ( w , & cookie )
w . Write ( [ ] byte ( "Login ok" ) )
} else {
w . Write ( [ ] byte ( "Login failed" ) )
}
}
2021-11-04 02:04:05 +00:00
func logout ( w http . ResponseWriter , r * http . Request ) {
cookie := http . Cookie {
Name : "jwt" ,
Value : "" ,
}
http . SetCookie ( w , & cookie )
w . Write ( [ ] byte ( "Du wurdest ausgeloggt" ) )
}
2021-11-04 01:14:51 +00:00
func register ( w http . ResponseWriter , r * http . Request ) {
r . ParseForm ( )
2021-11-24 02:27:07 +00:00
if captcha . VerifyString ( r . FormValue ( "captchaid" ) , r . FormValue ( "captcha" ) ) == false {
log . Debug ( ) . Str ( "captchaid" , r . FormValue ( "captchaid" ) ) . Str ( "input" , r . FormValue ( "captcha" ) ) . Interface ( "digits" , [ ] byte ( r . FormValue ( "captcha" ) ) ) . Msg ( "Captcha wrong" )
ren . HTML ( w , http . StatusOK , "register" , map [ string ] string { "captcha" : captcha . New ( ) , "msg" : "Captcha code was wrong" , "email" : r . FormValue ( "email" ) , "username" : r . FormValue ( "username" ) } )
return
}
if validEmail ( r . FormValue ( "email" ) ) == false {
ren . HTML ( w , http . StatusOK , "register" , map [ string ] string { "captcha" : captcha . New ( ) , "msg" : "E-Mail address is not valide." , "email" : r . FormValue ( "email" ) , "username" : r . FormValue ( "username" ) } )
return
}
matched , _ := regexp . MatchString ( ` ^[a-z0-9] { 5,25}$ ` , r . FormValue ( "username" ) )
if matched == false {
ren . HTML ( w , http . StatusOK , "register" , map [ string ] string { "captcha" : captcha . New ( ) , "msg" : "Username must be between 5 and 25 lowercase letters and numbers" , "email" : r . FormValue ( "email" ) , "username" : r . FormValue ( "username" ) } )
return
}
if len ( r . FormValue ( "password" ) ) < 8 {
ren . HTML ( w , http . StatusOK , "register" , map [ string ] string { "captcha" : captcha . New ( ) , "msg" : "Your password must be at least 8 letters" , "email" : r . FormValue ( "email" ) , "username" : r . FormValue ( "username" ) } )
return
}
2021-11-22 22:11:37 +00:00
res , err := uc . register ( r . FormValue ( "username" ) , r . FormValue ( "password" ) , r . FormValue ( "email" ) )
if err != nil || res == false {
2021-11-24 02:27:07 +00:00
ren . HTML ( w , http . StatusOK , "register" , map [ string ] string { "captcha" : captcha . New ( ) , "msg" : "Registration Failed" , "email" : r . FormValue ( "email" ) , "username" : r . FormValue ( "username" ) } )
2021-11-22 22:11:37 +00:00
return
2021-11-04 01:14:51 +00:00
}
2021-11-22 22:11:37 +00:00
rw := & LayoutMiddlewareResponseWriter {
ResponseWriter : w ,
buf : & bytes . Buffer { } ,
}
2021-11-23 19:21:43 +00:00
token , err := uc . getMailValidationToken ( r . FormValue ( "email" ) , true )
2021-11-22 22:11:37 +00:00
if err != nil {
panic ( err )
}
2021-11-24 02:27:07 +00:00
ren . HTML ( rw , http . StatusOK , "token" , map [ string ] string { "username" : r . FormValue ( "username" ) , "url" : fmt . Sprintf ( userconfig . URLValidationToken , string ( base64 . StdEncoding . EncodeToString ( [ ] byte ( r . FormValue ( "email" ) ) ) + "/" + token ) ) } )
2021-11-22 22:46:01 +00:00
2021-11-23 19:21:43 +00:00
content , err := io . ReadAll ( rw . buf )
if err != nil {
panic ( err )
}
2021-11-22 22:11:37 +00:00
2021-11-23 19:21:43 +00:00
mail := * userconfig . RegisterMail
mail . SetBody ( "text/html" , string ( content ) )
2021-11-24 02:27:07 +00:00
2021-11-23 19:21:43 +00:00
if err := d . DialAndSend ( & mail ) ; err != nil {
2021-11-22 22:11:37 +00:00
fmt . Println ( err )
panic ( err )
}
2021-11-24 02:27:07 +00:00
w . Write ( [ ] byte ( "registration successful, please check your mail" ) )
2021-11-22 22:11:37 +00:00
}
type LayoutMiddlewareResponseWriter struct {
http . ResponseWriter
buf * bytes . Buffer
}
func ( myrw * LayoutMiddlewareResponseWriter ) Write ( p [ ] byte ) ( int , error ) {
return myrw . buf . Write ( p )
2021-11-04 01:14:51 +00:00
}
2021-11-23 20:32:55 +00:00
2021-11-24 02:27:07 +00:00
func validEmail ( email string ) bool {
_ , err := mail . ParseAddress ( email )
return err == nil
}