forked from genofire/unified-push-xmpp
parent
531c267473
commit
88c59ac00b
|
@ -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
|
||||||
|
|
|
@ -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=
|
||||||
|
|
|
@ -13,6 +13,7 @@ import (
|
||||||
var VERSION = "development"
|
var VERSION = "development"
|
||||||
|
|
||||||
type configData struct {
|
type configData struct {
|
||||||
|
JWTSecret JWTSecret `toml"jwt_secret"`
|
||||||
EndpointURL string `toml:"endpoint_url"`
|
EndpointURL string `toml:"endpoint_url"`
|
||||||
XMPP XMPPService `toml:"xmpp"`
|
XMPP XMPPService `toml:"xmpp"`
|
||||||
Webserver web.Service `toml:"webserver"`
|
Webserver web.Service `toml:"webserver"`
|
||||||
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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{
|
||||||
|
|
|
@ -23,6 +23,7 @@ type XMPPService struct {
|
||||||
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:"-"`
|
||||||
|
JWTSecret JWTSecret `toml"-"`
|
||||||
session *xmpp.Session
|
session *xmpp.Session
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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,
|
||||||
|
|
Loading…
Reference in New Issue