Compare commits
2 Commits
ede2da8f8d
...
d91b3d72b3
Author | SHA1 | Date | |
---|---|---|---|
d91b3d72b3 | |||
dd1cf843e4 |
26
CHANGELOG.md
Normal file
26
CHANGELOG.md
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
# Changelog
|
||||||
|
|
||||||
|
## [Unreleased]
|
||||||
|
|
||||||
|
## [1.0.0-rc.1] - 2021-11-20
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Postgresql support for channels, feeds, items and subscriptions.
|
||||||
|
- Support "source" in items and feeds
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Default channel backend is postgresql
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- All `staticcheck` problems are fixed.
|
||||||
|
- Dependency between memorybackend and hubbackend removed and simplified.
|
||||||
|
|
||||||
|
### Deprecated
|
||||||
|
|
||||||
|
- All Redis timeline types are deprecated and will be removed in a later version.
|
||||||
|
|
||||||
|
[Unreleased]: https://git.p83.nl/peter/ekster/compare/1.0.0-rc.1...master
|
||||||
|
[1.0.0-rc.1]: https://git.p83.nl/peter/ekster/src/tag/1.0.0-rc.1
|
|
@ -242,7 +242,6 @@ func channelID(sub microsub.Microsub, channelNameOrID string) (string, error) {
|
||||||
// we encountered an error, so we are not sure if it worked
|
// we encountered an error, so we are not sure if it worked
|
||||||
return channelNameOrID, err
|
return channelNameOrID, err
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, c := range channels {
|
for _, c := range channels {
|
||||||
if c.Name == channelNameOrID {
|
if c.Name == channelNameOrID {
|
||||||
return c.UID, nil
|
return c.UID, nil
|
||||||
|
|
|
@ -29,9 +29,11 @@ import (
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/cristalhq/jwt/v4"
|
||||||
"p83.nl/go/ekster/pkg/indieauth"
|
"p83.nl/go/ekster/pkg/indieauth"
|
||||||
"p83.nl/go/ekster/pkg/microsub"
|
"p83.nl/go/ekster/pkg/microsub"
|
||||||
"p83.nl/go/ekster/pkg/util"
|
"p83.nl/go/ekster/pkg/util"
|
||||||
|
@ -44,7 +46,7 @@ import (
|
||||||
var templates embed.FS
|
var templates embed.FS
|
||||||
|
|
||||||
type mainHandler struct {
|
type mainHandler struct {
|
||||||
Backend *memoryBackend
|
Backend microsub.Microsub
|
||||||
BaseURL string
|
BaseURL string
|
||||||
TemplateDir string
|
TemplateDir string
|
||||||
pool *redis.Pool
|
pool *redis.Pool
|
||||||
|
@ -111,6 +113,12 @@ type authRequest struct {
|
||||||
AccessToken string `redis:"access_token"`
|
AccessToken string `redis:"access_token"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type micropubClaims struct {
|
||||||
|
jwt.RegisteredClaims
|
||||||
|
Channel string
|
||||||
|
Me string
|
||||||
|
}
|
||||||
|
|
||||||
func newMainHandler(backend *memoryBackend, baseURL, templateDir string, pool *redis.Pool) (*mainHandler, error) {
|
func newMainHandler(backend *memoryBackend, baseURL, templateDir string, pool *redis.Pool) (*mainHandler, error) {
|
||||||
h := &mainHandler{Backend: backend}
|
h := &mainHandler{Backend: backend}
|
||||||
|
|
||||||
|
@ -164,11 +172,13 @@ func loadSession(sessionVar string, conn redis.Conn) (session, error) {
|
||||||
sessionKey := "session:" + sessionVar
|
sessionKey := "session:" + sessionVar
|
||||||
data, err := redis.Values(conn.Do("HGETALL", sessionKey))
|
data, err := redis.Values(conn.Do("HGETALL", sessionKey))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
return sess, err
|
return sess, err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = redis.ScanStruct(data, &sess)
|
err = redis.ScanStruct(data, &sess)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
return sess, err
|
return sess, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -230,18 +240,19 @@ func verifyAuthCode(code, redirectURI, authEndpoint, clientID string) (bool, *au
|
||||||
return false, nil, fmt.Errorf("unknown content-type %q while verifying authorization_code", contentType)
|
return false, nil, fmt.Errorf("unknown content-type %q while verifying authorization_code", contentType)
|
||||||
}
|
}
|
||||||
|
|
||||||
func isLoggedIn(backend *memoryBackend, sess *session) bool {
|
func isLoggedIn(sess *session) bool {
|
||||||
if !sess.LoggedIn {
|
if !sess.LoggedIn {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if !backend.AuthEnabled {
|
// FIXME: Do we need this?
|
||||||
return true
|
// if !backend.AuthEnabled {
|
||||||
}
|
// return true
|
||||||
|
// }
|
||||||
if sess.Me != backend.Me {
|
//
|
||||||
return false
|
// if sess.Me != backend.Me {
|
||||||
}
|
// return false
|
||||||
|
// }
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -251,7 +262,6 @@ func performIndieauthCallback(clientID string, r *http.Request, sess *session) (
|
||||||
if state != sess.State {
|
if state != sess.State {
|
||||||
return false, &authResponse{}, fmt.Errorf("mismatched state")
|
return false, &authResponse{}, fmt.Errorf("mismatched state")
|
||||||
}
|
}
|
||||||
|
|
||||||
code := r.Form.Get("code")
|
code := r.Form.Get("code")
|
||||||
return verifyAuthCode(code, sess.RedirectURI, sess.AuthorizationEndpoint, clientID)
|
return verifyAuthCode(code, sess.RedirectURI, sess.AuthorizationEndpoint, clientID)
|
||||||
}
|
}
|
||||||
|
@ -377,7 +387,7 @@ func (h *mainHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !isLoggedIn(h.Backend, &sess) {
|
if !isLoggedIn(&sess) {
|
||||||
w.WriteHeader(401)
|
w.WriteHeader(401)
|
||||||
fmt.Fprintf(w, "Unauthorized")
|
fmt.Fprintf(w, "Unauthorized")
|
||||||
return
|
return
|
||||||
|
@ -402,14 +412,14 @@ func (h *mainHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
for _, v := range page.Channels {
|
for _, v := range page.Channels {
|
||||||
if v.UID == currentChannel {
|
if v.UID == currentChannel {
|
||||||
page.CurrentChannel = v
|
page.CurrentChannel = v
|
||||||
if setting, e := h.Backend.Settings[v.UID]; e {
|
// if setting, e := h.Backend.Settings[v.UID]; e {
|
||||||
page.CurrentSetting = setting
|
// page.CurrentSetting = setting
|
||||||
} else {
|
// } else {
|
||||||
page.CurrentSetting = channelSetting{}
|
page.CurrentSetting = channelSetting{}
|
||||||
}
|
// }
|
||||||
if page.CurrentSetting.ChannelType == "" {
|
// if page.CurrentSetting.ChannelType == "" {
|
||||||
page.CurrentSetting.ChannelType = "postgres-stream"
|
page.CurrentSetting.ChannelType = "postgres-stream"
|
||||||
}
|
// }
|
||||||
page.ExcludedTypeNames = map[string]string{
|
page.ExcludedTypeNames = map[string]string{
|
||||||
"repost": "Reposts",
|
"repost": "Reposts",
|
||||||
"like": "Likes",
|
"like": "Likes",
|
||||||
|
@ -448,7 +458,7 @@ func (h *mainHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !isLoggedIn(h.Backend, &sess) {
|
if !isLoggedIn(&sess) {
|
||||||
w.WriteHeader(401)
|
w.WriteHeader(401)
|
||||||
fmt.Fprintf(w, "Unauthorized")
|
fmt.Fprintf(w, "Unauthorized")
|
||||||
return
|
return
|
||||||
|
@ -476,7 +486,7 @@ func (h *mainHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !isLoggedIn(h.Backend, &sess) {
|
if !isLoggedIn(&sess) {
|
||||||
w.WriteHeader(401)
|
w.WriteHeader(401)
|
||||||
fmt.Fprintf(w, "Unauthorized")
|
fmt.Fprintf(w, "Unauthorized")
|
||||||
return
|
return
|
||||||
|
@ -498,9 +508,39 @@ func (h *mainHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
} else if r.URL.Path == "/auth" {
|
} else if r.URL.Path == "/auth" {
|
||||||
// check if we are logged in
|
// check arguments for auth
|
||||||
// TODO: if not logged in, make sure we get back here
|
query := r.URL.Query()
|
||||||
|
responseType := query.Get("response_type")
|
||||||
|
if responseType != "code" {
|
||||||
|
http.Error(w, "Unsupported response_type", 400)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
redirectURI := query.Get("redirect_uri")
|
||||||
|
if _, err := url.Parse(redirectURI); err != nil {
|
||||||
|
http.Error(w, "Missing redirect_uri", 400)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
clientID := query.Get("client_id")
|
||||||
|
if _, err := url.Parse(clientID); err != nil {
|
||||||
|
http.Error(w, "Missing client_id", 400)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
me := query.Get("me")
|
||||||
|
if _, err := url.Parse(me); err != nil {
|
||||||
|
http.Error(w, "Missing me", 400)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
state := query.Get("state")
|
||||||
|
scope := query.Get("scope")
|
||||||
|
if scope == "" {
|
||||||
|
scope = "create"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the client is logging in
|
||||||
sessionVar := getSessionCookie(w, r)
|
sessionVar := getSessionCookie(w, r)
|
||||||
|
|
||||||
sess, err := loadSession(sessionVar, conn)
|
sess, err := loadSession(sessionVar, conn)
|
||||||
|
@ -510,7 +550,7 @@ func (h *mainHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !isLoggedIn(h.Backend, &sess) {
|
if !isLoggedIn(&sess) {
|
||||||
sess.NextURI = r.URL.String()
|
sess.NextURI = r.URL.String()
|
||||||
saveSession(sessionVar, &sess, conn)
|
saveSession(sessionVar, &sess, conn)
|
||||||
http.Redirect(w, r, "/", http.StatusFound)
|
http.Redirect(w, r, "/", http.StatusFound)
|
||||||
|
@ -520,18 +560,6 @@ func (h *mainHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
sess.NextURI = r.URL.String()
|
sess.NextURI = r.URL.String()
|
||||||
saveSession(sessionVar, &sess, conn)
|
saveSession(sessionVar, &sess, conn)
|
||||||
|
|
||||||
query := r.URL.Query()
|
|
||||||
|
|
||||||
// responseType := query.Get("response_type") // TODO: check response_type
|
|
||||||
me := query.Get("me")
|
|
||||||
clientID := query.Get("client_id")
|
|
||||||
redirectURI := query.Get("redirect_uri")
|
|
||||||
state := query.Get("state")
|
|
||||||
scope := query.Get("scope")
|
|
||||||
if scope == "" {
|
|
||||||
scope = "create"
|
|
||||||
}
|
|
||||||
|
|
||||||
authReq := authRequest{
|
authReq := authRequest{
|
||||||
Me: me,
|
Me: me,
|
||||||
ClientID: clientID,
|
ClientID: clientID,
|
||||||
|
@ -585,6 +613,18 @@ func (h *mainHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
// redirect to endpoint
|
// redirect to endpoint
|
||||||
me := r.Form.Get("url")
|
me := r.Form.Get("url")
|
||||||
|
if !strings.HasPrefix("https://", me) {
|
||||||
|
me = fmt.Sprintf("https://%s", me)
|
||||||
|
}
|
||||||
|
meURL, err := url.Parse(me)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, fmt.Sprintf("Not a url: %s", me), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if meURL.Path == "" {
|
||||||
|
meURL.Path = "/"
|
||||||
|
}
|
||||||
|
me = meURL.String()
|
||||||
|
|
||||||
endpoints, err := getEndpoints(me)
|
endpoints, err := getEndpoints(me)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -695,17 +735,31 @@ func (h *mainHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
fmt.Fprintf(w, "ERROR: %q", err)
|
fmt.Fprintf(w, "ERROR: %q", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
token := util.RandStringBytes(32)
|
|
||||||
_, err = conn.Do("HMSET", redis.Args{}.Add("token:"+token).AddFlat(&auth)...)
|
key := []byte(os.Getenv("APP_SECRET"))
|
||||||
|
signer, err := jwt.NewSignerHS(jwt.HS256, key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
log.Printf("could not create signer for jwt: %s", err)
|
||||||
fmt.Fprintf(w, "ERROR: %q", err)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
builder := jwt.NewBuilder(signer)
|
||||||
|
token, err := builder.Build(µpubClaims{
|
||||||
|
RegisteredClaims: jwt.RegisteredClaims{
|
||||||
|
IssuedAt: jwt.NewNumericDate(time.Now()),
|
||||||
|
},
|
||||||
|
Channel: auth.Channel,
|
||||||
|
Me: auth.Me,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("could not create signer for jwt: %s", err)
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
res := authTokenResponse{
|
res := authTokenResponse{
|
||||||
Me: auth.Me,
|
Me: auth.Me,
|
||||||
AccessToken: token,
|
AccessToken: token.String(),
|
||||||
TokenType: "Bearer",
|
TokenType: "Bearer",
|
||||||
Scope: auth.Scope,
|
Scope: auth.Scope,
|
||||||
}
|
}
|
||||||
|
|
95
cmd/eksterd/http_test.go
Normal file
95
cmd/eksterd/http_test.go
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
/*
|
||||||
|
* Ekster is a microsub server
|
||||||
|
* Copyright (c) 2021 The Ekster authors
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"math/rand"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"net/url"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/gomodule/redigo/redis"
|
||||||
|
"github.com/rafaeljusto/redigomock/v3"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"p83.nl/go/ekster/pkg/server"
|
||||||
|
"p83.nl/go/ekster/pkg/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
rand.Seed(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMainHandler_ServeHTTP_NoArgs(t *testing.T) {
|
||||||
|
conn := redigomock.NewConn()
|
||||||
|
pool := &redis.Pool{
|
||||||
|
// Return the same connection mock for each Get() call.
|
||||||
|
Dial: func() (redis.Conn, error) { return conn, nil },
|
||||||
|
MaxIdle: 10,
|
||||||
|
}
|
||||||
|
|
||||||
|
h := mainHandler{Backend: &memoryBackend{AuthEnabled: true}, BaseURL: "", TemplateDir: "", pool: pool}
|
||||||
|
r := httptest.NewRequest(http.MethodGet, "/auth", nil)
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
h.ServeHTTP(w, r)
|
||||||
|
assert.Equal(t, 400, w.Code)
|
||||||
|
}
|
||||||
|
func TestMainHandler_ServeHTTP_Args(t *testing.T) {
|
||||||
|
conn := redigomock.NewConn()
|
||||||
|
pool := &redis.Pool{
|
||||||
|
// Return the same connection mock for each Get() call.
|
||||||
|
Dial: func() (redis.Conn, error) { return conn, nil },
|
||||||
|
MaxIdle: 10,
|
||||||
|
}
|
||||||
|
|
||||||
|
q := url.Values{}
|
||||||
|
q.Add("response_type", "code")
|
||||||
|
q.Add("client_id", "https://example.com/")
|
||||||
|
q.Add("redirect_uri", "https://example.com/callback")
|
||||||
|
q.Add("me", "https://p83.nl/")
|
||||||
|
state := util.RandStringBytes(32)
|
||||||
|
q.Add("state", state)
|
||||||
|
q.Add("scope", "create")
|
||||||
|
h := mainHandler{Backend: &server.NullBackend{}, BaseURL: "", TemplateDir: "", pool: pool}
|
||||||
|
r := httptest.NewRequest(http.MethodGet, "/auth?"+q.Encode(), nil)
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
|
||||||
|
conn.Command("HGETALL", "session:FpLSjFbcXoEFfRsW").ExpectMap(map[string]string{
|
||||||
|
"logged_in": "1",
|
||||||
|
})
|
||||||
|
conn.Command(
|
||||||
|
"HMSET", "state:XVlBzgbaiCMRAjWwhTHctcuAxhxKQFDa",
|
||||||
|
"me", "https://p83.nl/",
|
||||||
|
"client_id", "https://example.com/",
|
||||||
|
"scope", "create",
|
||||||
|
"redirect_uri", "https://example.com/callback",
|
||||||
|
"state", "XVlBzgbaiCMRAjWwhTHctcuAxhxKQFDa",
|
||||||
|
"code", "",
|
||||||
|
"channel", "",
|
||||||
|
"access_token", "",
|
||||||
|
)
|
||||||
|
|
||||||
|
h.ServeHTTP(w, r)
|
||||||
|
assert.Equal(t, 302, w.Code)
|
||||||
|
body, err := ioutil.ReadAll(w.Result().Body)
|
||||||
|
if assert.NoError(t, err) {
|
||||||
|
assert.Equal(t, "", string(body))
|
||||||
|
}
|
||||||
|
}
|
|
@ -24,6 +24,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -33,6 +34,8 @@ import (
|
||||||
"github.com/gomodule/redigo/redis"
|
"github.com/gomodule/redigo/redis"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"willnorris.com/go/microformats"
|
"willnorris.com/go/microformats"
|
||||||
|
|
||||||
|
"github.com/cristalhq/jwt/v4"
|
||||||
)
|
)
|
||||||
|
|
||||||
type micropubHandler struct {
|
type micropubHandler struct {
|
||||||
|
@ -183,6 +186,11 @@ func getChannelFromAuthorization(r *http.Request, conn redis.Conn) (string, erro
|
||||||
token := authHeader[7:]
|
token := authHeader[7:]
|
||||||
channel, err := redis.String(conn.Do("HGET", "token:"+token, "channel"))
|
channel, err := redis.String(conn.Do("HGET", "token:"+token, "channel"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
_, err := verifyJWT(token)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
return "", errors.Wrap(err, "could not get channel for token")
|
return "", errors.Wrap(err, "could not get channel for token")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -191,3 +199,28 @@ func getChannelFromAuthorization(r *http.Request, conn redis.Conn) (string, erro
|
||||||
|
|
||||||
return "", fmt.Errorf("could not get channel from authorization")
|
return "", fmt.Errorf("could not get channel from authorization")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func verifyJWT(token string) (bool, error) {
|
||||||
|
key := []byte(os.Getenv("APP_SECRET"))
|
||||||
|
verifier, err := jwt.NewVerifierHS(jwt.HS256, key)
|
||||||
|
if err != nil {
|
||||||
|
return false, fmt.Errorf("could not create verifier for jwt: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
newToken, err := jwt.Parse([]byte(token), verifier)
|
||||||
|
if err != nil {
|
||||||
|
return false, fmt.Errorf("could not parse jwt: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var newClaims jwt.RegisteredClaims
|
||||||
|
err = json.Unmarshal(newToken.Claims(), &newClaims)
|
||||||
|
if err != nil {
|
||||||
|
return false, fmt.Errorf("could not parse jwt claims: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !newClaims.IsValidAt(time.Now()) {
|
||||||
|
return false, fmt.Errorf("jwt is not valid now")
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
4
go.mod
4
go.mod
|
@ -5,12 +5,14 @@ go 1.16
|
||||||
require (
|
require (
|
||||||
github.com/axgle/mahonia v0.0.0-20180208002826-3358181d7394
|
github.com/axgle/mahonia v0.0.0-20180208002826-3358181d7394
|
||||||
github.com/blevesearch/bleve/v2 v2.0.3
|
github.com/blevesearch/bleve/v2 v2.0.3
|
||||||
|
github.com/cristalhq/jwt/v4 v4.0.0-beta // indirect
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
github.com/gilliek/go-opml v1.0.0
|
github.com/gilliek/go-opml v1.0.0
|
||||||
github.com/golang-migrate/migrate/v4 v4.15.1
|
github.com/golang-migrate/migrate/v4 v4.15.1
|
||||||
github.com/gomodule/redigo v1.8.2
|
github.com/gomodule/redigo v1.8.5
|
||||||
github.com/lib/pq v1.10.1
|
github.com/lib/pq v1.10.1
|
||||||
github.com/pkg/errors v0.9.1
|
github.com/pkg/errors v0.9.1
|
||||||
|
github.com/rafaeljusto/redigomock/v3 v3.0.1 // indirect
|
||||||
github.com/stretchr/testify v1.7.0
|
github.com/stretchr/testify v1.7.0
|
||||||
golang.org/x/net v0.0.0-20211013171255-e13a2654a71e
|
golang.org/x/net v0.0.0-20211013171255-e13a2654a71e
|
||||||
willnorris.com/go/microformats v1.1.0
|
willnorris.com/go/microformats v1.1.0
|
||||||
|
|
7
go.sum
7
go.sum
|
@ -312,6 +312,8 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:ma
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||||
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
||||||
github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||||
|
github.com/cristalhq/jwt/v4 v4.0.0-beta h1:TSNUSW7CCvMtX+Rg3vj706BwqIEt9HMvVv6Syifq5mU=
|
||||||
|
github.com/cristalhq/jwt/v4 v4.0.0-beta/go.mod h1:HnYraSNKDRag1DZP92rYHyrjyQHnVEHPNqesmzs+miQ=
|
||||||
github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4=
|
github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4=
|
||||||
github.com/cznic/mathutil v0.0.0-20180504122225-ca4c9f2c1369/go.mod h1:e6NPNENfs9mPDVNRekM7lKScauxd5kXTr1Mfyig6TDM=
|
github.com/cznic/mathutil v0.0.0-20180504122225-ca4c9f2c1369/go.mod h1:e6NPNENfs9mPDVNRekM7lKScauxd5kXTr1Mfyig6TDM=
|
||||||
github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1SMSibvLzxjeJLnrYEVLULFNiHY9YfQ=
|
github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1SMSibvLzxjeJLnrYEVLULFNiHY9YfQ=
|
||||||
|
@ -496,6 +498,9 @@ github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
|
||||||
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||||
github.com/gomodule/redigo v1.8.2 h1:H5XSIre1MB5NbPYFp+i1NBbb5qN1W8Y8YAQoAYbkm8k=
|
github.com/gomodule/redigo v1.8.2 h1:H5XSIre1MB5NbPYFp+i1NBbb5qN1W8Y8YAQoAYbkm8k=
|
||||||
github.com/gomodule/redigo v1.8.2/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0=
|
github.com/gomodule/redigo v1.8.2/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0=
|
||||||
|
github.com/gomodule/redigo v1.8.3/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0=
|
||||||
|
github.com/gomodule/redigo v1.8.5 h1:nRAxCa+SVsyjSBrtZmG/cqb6VbTmuRzpg/PoTFlpumc=
|
||||||
|
github.com/gomodule/redigo v1.8.5/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0=
|
||||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||||
github.com/google/flatbuffers v2.0.0+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
|
github.com/google/flatbuffers v2.0.0+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
|
||||||
|
@ -838,6 +843,8 @@ github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4O
|
||||||
github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||||
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||||
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||||
|
github.com/rafaeljusto/redigomock/v3 v3.0.1 h1:AUsXTuf+UEMwVEgRHRDYFFCJ1quS2JVDQmTWypjI5mI=
|
||||||
|
github.com/rafaeljusto/redigomock/v3 v3.0.1/go.mod h1:51LNR7Q4YFsi0N+CHr7+FC1Jx2lPLzcRHCPlLO2Qbpw=
|
||||||
github.com/rcrowley/go-metrics v0.0.0-20190826022208-cac0b30c2563/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
github.com/rcrowley/go-metrics v0.0.0-20190826022208-cac0b30c2563/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20190728182440-6a916e37a237/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
github.com/remyoudompheng/bigfft v0.0.0-20190728182440-6a916e37a237/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||||
|
|
|
@ -155,6 +155,7 @@ type Microsub interface {
|
||||||
ItemSearch(channel, query string) ([]Item, error)
|
ItemSearch(channel, query string) ([]Item, error)
|
||||||
|
|
||||||
Events() (chan sse.Message, error)
|
Events() (chan sse.Message, error)
|
||||||
|
RefreshFeeds()
|
||||||
}
|
}
|
||||||
|
|
||||||
// MarshalJSON encodes an Unread value as JSON
|
// MarshalJSON encodes an Unread value as JSON
|
||||||
|
|
|
@ -27,6 +27,12 @@ import (
|
||||||
type NullBackend struct {
|
type NullBackend struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RefreshFeeds refreshes feeds
|
||||||
|
func (b *NullBackend) RefreshFeeds() {
|
||||||
|
// TODO implement me
|
||||||
|
// panic("implement me")
|
||||||
|
}
|
||||||
|
|
||||||
// ChannelsGetList gets no channels
|
// ChannelsGetList gets no channels
|
||||||
func (b *NullBackend) ChannelsGetList() ([]microsub.Channel, error) {
|
func (b *NullBackend) ChannelsGetList() ([]microsub.Channel, error) {
|
||||||
return []microsub.Channel{
|
return []microsub.Channel{
|
||||||
|
|
Loading…
Reference in New Issue
Block a user