implement JWT for anti spam

fix #1
This commit is contained in:
Geno 2021-09-14 01:32:37 +02:00
parent 531c267473
commit 88c59ac00b
8 changed files with 112 additions and 20 deletions

View File

@ -25,6 +25,7 @@ require (
github.com/go-playground/locales v0.13.0 // indirect github.com/go-playground/locales v0.13.0 // indirect
github.com/go-playground/universal-translator v0.17.0 // indirect github.com/go-playground/universal-translator v0.17.0 // indirect
github.com/go-playground/validator/v10 v10.4.1 // indirect github.com/go-playground/validator/v10 v10.4.1 // indirect
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
github.com/golang/protobuf v1.4.3 // indirect github.com/golang/protobuf v1.4.3 // indirect
github.com/gorilla/context v1.1.1 // indirect github.com/gorilla/context v1.1.1 // indirect
github.com/gorilla/securecookie v1.1.1 // indirect github.com/gorilla/securecookie v1.1.1 // indirect

View File

@ -123,6 +123,8 @@ github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFG
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=

View File

@ -13,9 +13,10 @@ import (
var VERSION = "development" var VERSION = "development"
type configData struct { type configData struct {
EndpointURL string `toml:"endpoint_url"` JWTSecret JWTSecret `toml"jwt_secret"`
XMPP XMPPService `toml:"xmpp"` EndpointURL string `toml:"endpoint_url"`
Webserver web.Service `toml:"webserver"` XMPP XMPPService `toml:"xmpp"`
Webserver web.Service `toml:"webserver"`
} }
func main() { func main() {
@ -42,6 +43,7 @@ func main() {
} }
// just for more beautiful config file - jere // just for more beautiful config file - jere
config.XMPP.EndpointURL = config.EndpointURL config.XMPP.EndpointURL = config.EndpointURL
config.XMPP.JWTSecret = config.JWTSecret
go func() { go func() {
if err := config.XMPP.Run(); err != nil { if err := config.XMPP.Run(); err != nil {
@ -49,7 +51,7 @@ func main() {
} }
}() }()
config.Webserver.ModuleRegister(Bind(&config.XMPP)) config.Webserver.ModuleRegister(Bind(&config.XMPP, config.JWTSecret))
log.Info("startup") log.Info("startup")
if err := config.Webserver.Run(); err != nil { if err := config.Webserver.Run(); err != nil {

49
gateway/token.go Normal file
View File

@ -0,0 +1,49 @@
package main
import (
"github.com/golang-jwt/jwt"
"mellium.im/xmpp/jid"
)
// JWTSecret the secret
type JWTSecret string
// JWTToken data field
type JWTToken struct {
jwt.StandardClaims
Token string `json:"token"`
JID string `json:"jid"`
}
// Generate an jwt token by token and jid
func (s JWTSecret) Generate(jid jid.JID, token string) (string, error) {
jwtToken := JWTToken{
Token: token,
JID: jid.String(),
}
claim := jwt.NewWithClaims(jwt.SigningMethodHS512, jwtToken)
t, err := claim.SignedString([]byte(s))
if err != nil {
return "", err
}
return t, nil
}
// Read token to token and jid
func (s JWTSecret) Read(jwtToken string) (jid.JID, string, error) {
token, err := jwt.ParseWithClaims(jwtToken, &JWTToken{}, func(token *jwt.Token) (interface{}, error) {
return []byte(s), nil
})
if err != nil {
return jid.JID{}, "", err
}
claims, ok := token.Claims.(*JWTToken)
if !ok {
return jid.JID{}, "", jwt.ErrInvalidKey
}
addr, err := jid.Parse(claims.JID)
if err != nil {
return jid.JID{}, "", err
}
return addr, claims.Token, nil
}

25
gateway/token_test.go Normal file
View File

@ -0,0 +1,25 @@
package main
import (
"testing"
"github.com/stretchr/testify/assert"
"mellium.im/xmpp/jid"
)
func TestJWT(t *testing.T) {
assert := assert.New(t)
addr := "a@example.org"
token := "pushtoken"
secret := JWTSecret("CHANGEME")
jwt, err := secret.Generate(jid.MustParse(addr), token)
assert.NoError(err)
assert.NoEqual("", jwt)
jid, t, err := secret.Read(jwt)
assert.NoError(err)
assert.Equal(addr, jid.String())
assert.Equal(t, token)
}

View File

@ -1,10 +1,10 @@
package main package main
import ( import (
"github.com/gin-gonic/gin"
"dev.sum7.eu/genofire/golang-lib/web" "dev.sum7.eu/genofire/golang-lib/web"
"dev.sum7.eu/genofire/golang-lib/web/api/status" "dev.sum7.eu/genofire/golang-lib/web/api/status"
"dev.sum7.eu/genofire/golang-lib/web/metrics" "dev.sum7.eu/genofire/golang-lib/web/metrics"
"github.com/gin-gonic/gin"
) )
// Bind to webservice // Bind to webservice
@ -18,13 +18,13 @@ import (
// @securityDefinitions.apikey ApiKeyAuth // @securityDefinitions.apikey ApiKeyAuth
// @in header // @in header
// @name Authorization // @name Authorization
func Bind(xmpp *XMPPService) web.ModuleRegisterFunc { func Bind(xmpp *XMPPService, jwt JWTSecret) web.ModuleRegisterFunc {
return func(r *gin.Engine, ws *web.Service) { return func(r *gin.Engine, ws *web.Service) {
// docs.Bind(r, ws) // docs.Bind(r, ws)
status.Register(r, ws) status.Register(r, ws)
metrics.Register(r, ws) metrics.Register(r, ws)
Get(r, ws) Get(r, ws)
Post(r, ws, xmpp) Post(r, ws, xmpp, jwt)
} }
} }

View File

@ -2,16 +2,22 @@ package main
import ( import (
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"net/http"
"io/ioutil" "io/ioutil"
"net/http"
"dev.sum7.eu/genofire/golang-lib/web" "dev.sum7.eu/genofire/golang-lib/web"
) )
func Post(r *gin.Engine, ws *web.Service, xmpp *XMPPService) { func Post(r *gin.Engine, ws *web.Service, xmpp *XMPPService, jwtsecret JWTSecret) {
r.POST("/UP", func(c *gin.Context) { r.POST("/UP", func(c *gin.Context) {
to := c.Query("to") to, token, err := jwtsecret.Read(c.Query("token"))
token := c.Query("token") if err != nil {
c.JSON(http.StatusUnauthorized, web.HTTPError{
Message: "jwt token unauthoried - or not given",
Error: err.Error(),
})
return
}
b, err := ioutil.ReadAll(c.Request.Body) b, err := ioutil.ReadAll(c.Request.Body)
if err != nil { if err != nil {
c.JSON(http.StatusBadRequest, web.HTTPError{ c.JSON(http.StatusBadRequest, web.HTTPError{

View File

@ -18,12 +18,13 @@ import (
) )
type XMPPService struct { type XMPPService struct {
Addr string `toml:"address"` Addr string `toml:"address"`
JID string `toml:"jid"` JID string `toml:"jid"`
Secret string `toml:"secret"` Secret string `toml:"secret"`
// hidden here for beautiful config file // hidden here for beautiful config file
EndpointURL string `toml:"-"` EndpointURL string `toml:"-"`
session *xmpp.Session JWTSecret JWTSecret `toml"-"`
session *xmpp.Session
} }
func (s *XMPPService) Run() error { func (s *XMPPService) Run() error {
@ -102,7 +103,13 @@ func (s *XMPPService) handleRegister(iq stanza.IQ, t xmlstream.TokenReadEncoder,
reply.Register.Error = &messages.ErrorData{Body: "no token"} reply.Register.Error = &messages.ErrorData{Body: "no token"}
return nil return nil
} }
endpoint := s.EndpointURL+"/UP?token=" + token + "&to=" + iq.From.String() jwt, err := s.JWTSecret.Generate(iq.From, token)
if err != nil {
log.Errorf("unable jwt generation: %v", err)
reply.Register.Error = &messages.ErrorData{Body: "jwt error on gateway"}
return nil
}
endpoint := s.EndpointURL + "/UP?token=" + jwt
reply.IQ.Type = stanza.ResultIQ reply.IQ.Type = stanza.ResultIQ
reply.Register.Endpoint = &messages.EndpointData{Body: endpoint} reply.Register.Endpoint = &messages.EndpointData{Body: endpoint}
log.Infof("generate respone: %v", endpoint) log.Infof("generate respone: %v", endpoint)
@ -145,14 +152,14 @@ func (s *XMPPService) handleDisco(iq stanza.IQ, t xmlstream.TokenReadEncoder, st
} }
// SendMessage of an UP Notification // SendMessage of an UP Notification
func (s *XMPPService) SendMessage(to, token, content string) error { func (s *XMPPService) SendMessage(to jid.JID, token, content string) error {
log.WithFields(map[string]interface{}{ log.WithFields(map[string]interface{}{
"to": to, "to": to,
"token": token, "token": token,
}).Info("forward message to xmpp") }).Info("forward message to xmpp")
return s.session.Encode(context.TODO(), messages.Message{ return s.session.Encode(context.TODO(), messages.Message{
Message: stanza.Message{ Message: stanza.Message{
To: jid.MustParse(to), To: to,
From: jid.MustParse(s.JID), From: jid.MustParse(s.JID),
// Type: stanza.ChatMessage, // Type: stanza.ChatMessage,
Type: stanza.NormalMessage, Type: stanza.NormalMessage,