getting there.......
This commit is contained in:
parent
9d5fb0785f
commit
6eab00e05e
4
go.mod
4
go.mod
|
@ -3,12 +3,12 @@ module github.com/gotosocial/gotosocial
|
||||||
go 1.16
|
go 1.16
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/gin-contrib/sessions v0.0.3 // indirect
|
github.com/gin-contrib/sessions v0.0.3
|
||||||
github.com/gin-gonic/gin v1.6.3
|
github.com/gin-gonic/gin v1.6.3
|
||||||
github.com/go-fed/activity v1.0.0
|
github.com/go-fed/activity v1.0.0
|
||||||
github.com/go-pg/pg/extra/pgdebug v0.2.0
|
github.com/go-pg/pg/extra/pgdebug v0.2.0
|
||||||
github.com/go-pg/pg/v10 v10.8.0
|
github.com/go-pg/pg/v10 v10.8.0
|
||||||
github.com/go-session/session v3.1.2+incompatible // indirect
|
github.com/go-session/session v3.1.2+incompatible
|
||||||
github.com/golang/mock v1.4.4 // indirect
|
github.com/golang/mock v1.4.4 // indirect
|
||||||
github.com/google/uuid v1.2.0 // indirect
|
github.com/google/uuid v1.2.0 // indirect
|
||||||
github.com/gotosocial/oauth2/v4 v4.2.1-0.20210316171520-7b12112bbb88
|
github.com/gotosocial/oauth2/v4 v4.2.1-0.20210316171520-7b12112bbb88
|
||||||
|
|
3
go.sum
3
go.sum
|
@ -105,8 +105,6 @@ github.com/gorilla/sessions v1.1.3 h1:uXoZdcdA5XdXF3QzuSlheVRUvjl+1rKY7zBXL68L9R
|
||||||
github.com/gorilla/sessions v1.1.3/go.mod h1:8KCfur6+4Mqcc6S0FEfKuN15Vl5MgXW92AE8ovaJD0w=
|
github.com/gorilla/sessions v1.1.3/go.mod h1:8KCfur6+4Mqcc6S0FEfKuN15Vl5MgXW92AE8ovaJD0w=
|
||||||
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
||||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
github.com/gotosocial/oauth2/v4 v4.2.1-0.20210315164102-1f7842217e57 h1:+zKsBEkg1cbz7zJDms1KMU9vJBeBAlElS1SbK/x0Rvc=
|
|
||||||
github.com/gotosocial/oauth2/v4 v4.2.1-0.20210315164102-1f7842217e57/go.mod h1:zl5kwHf/atRUrY5yOyDnk49Us1Ygs0BzdW4jKAgoiP8=
|
|
||||||
github.com/gotosocial/oauth2/v4 v4.2.1-0.20210316171520-7b12112bbb88 h1:YJ//HmHOYJ4srm/LA6VPNjNisneMbY6TTM1xttV/ZQU=
|
github.com/gotosocial/oauth2/v4 v4.2.1-0.20210316171520-7b12112bbb88 h1:YJ//HmHOYJ4srm/LA6VPNjNisneMbY6TTM1xttV/ZQU=
|
||||||
github.com/gotosocial/oauth2/v4 v4.2.1-0.20210316171520-7b12112bbb88/go.mod h1:zl5kwHf/atRUrY5yOyDnk49Us1Ygs0BzdW4jKAgoiP8=
|
github.com/gotosocial/oauth2/v4 v4.2.1-0.20210316171520-7b12112bbb88/go.mod h1:zl5kwHf/atRUrY5yOyDnk49Us1Ygs0BzdW4jKAgoiP8=
|
||||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||||
|
@ -163,6 +161,7 @@ github.com/onsi/gomega v1.10.5/go.mod h1:gza4q3jKQJijlu05nKWRCW/GavJumGt8aNRxWg7
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
|
github.com/quasoft/memstore v0.0.0-20180925164028-84a050167438 h1:jnz/4VenymvySjE+Ez511s0pqVzkUOmr1fwCVytNNWk=
|
||||||
github.com/quasoft/memstore v0.0.0-20180925164028-84a050167438/go.mod h1:wTPjTepVu7uJBYgZ0SdWHQlIas582j6cn2jgk4DDdlg=
|
github.com/quasoft/memstore v0.0.0-20180925164028-84a050167438/go.mod h1:wTPjTepVu7uJBYgZ0SdWHQlIas582j6cn2jgk4DDdlg=
|
||||||
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
|
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
|
||||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
|
|
|
@ -69,8 +69,8 @@ func (s *server) AttachHandler(method string, path string, handler gin.HandlerFu
|
||||||
|
|
||||||
func New(config *config.Config, logger *logrus.Logger) Server {
|
func New(config *config.Config, logger *logrus.Logger) Server {
|
||||||
engine := gin.New()
|
engine := gin.New()
|
||||||
store := memstore.NewStore([]byte("authentication-key"), []byte("encryption-key"))
|
store := memstore.NewStore([]byte("authentication-key"), []byte("encryption-keyencryption-key----"))
|
||||||
engine.Use(sessions.Sessions("mysession", store))
|
engine.Use(sessions.Sessions("gotosocial-session", store))
|
||||||
return &server{
|
return &server{
|
||||||
APIGroup: engine.Group("/api").Group("/v1"),
|
APIGroup: engine.Group("/api").Group("/v1"),
|
||||||
logger: logger,
|
logger: logger,
|
||||||
|
|
|
@ -19,16 +19,12 @@
|
||||||
package oauth
|
package oauth
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/gin-contrib/sessions"
|
"github.com/gin-contrib/sessions"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/go-pg/pg/v10"
|
"github.com/go-pg/pg/v10"
|
||||||
"github.com/go-session/session"
|
|
||||||
"github.com/gotosocial/gotosocial/internal/api"
|
"github.com/gotosocial/gotosocial/internal/api"
|
||||||
"github.com/gotosocial/gotosocial/internal/gtsmodel"
|
"github.com/gotosocial/gotosocial/internal/gtsmodel"
|
||||||
"github.com/gotosocial/oauth2/v4"
|
"github.com/gotosocial/oauth2/v4"
|
||||||
|
@ -48,6 +44,19 @@ type API struct {
|
||||||
log *logrus.Logger
|
log *logrus.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type login struct {
|
||||||
|
Username string `form:"username"`
|
||||||
|
Password string `form:"password"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type authorize struct {
|
||||||
|
ForceLogin string `form:"force_login,omitempty"`
|
||||||
|
ResponseType string `form:"response_type"`
|
||||||
|
ClientID string `form:"client_id"`
|
||||||
|
RedirectURI string `form:"redirect_uri"`
|
||||||
|
Scope string `form:"scope,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
func New(ts oauth2.TokenStore, cs oauth2.ClientStore, conn *pg.DB, log *logrus.Logger) *API {
|
func New(ts oauth2.TokenStore, cs oauth2.ClientStore, conn *pg.DB, log *logrus.Logger) *API {
|
||||||
manager := manage.NewDefaultManager()
|
manager := manage.NewDefaultManager()
|
||||||
manager.MapTokenStorage(ts)
|
manager.MapTokenStorage(ts)
|
||||||
|
@ -77,10 +86,11 @@ func New(ts oauth2.TokenStore, cs oauth2.ClientStore, conn *pg.DB, log *logrus.L
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *API) AddRoutes(s api.Server) error {
|
func (a *API) AddRoutes(s api.Server) error {
|
||||||
s.AttachHandler(methodAny, "/auth/sign_in", a.SignInHandler)
|
s.AttachHandler(http.MethodGet, "/auth/sign_in", a.SignInGETHandler)
|
||||||
s.AttachHandler(methodAny, "/oauth/token", gin.WrapF(a.TokenHandler))
|
s.AttachHandler(http.MethodPost, "/auth/sign_in", a.SignInPOSTHandler)
|
||||||
s.AttachHandler(methodAny, "/oauth/authorize", gin.WrapF(a.AuthorizeHandler))
|
s.AttachHandler(methodAny, "/oauth/token", a.TokenHandler)
|
||||||
s.AttachHandler(methodAny, "/auth", gin.WrapF(a.AuthHandler))
|
s.AttachHandler(http.MethodGet, "/oauth/authorize", a.AuthorizeHandler)
|
||||||
|
s.AttachHandler(methodAny, "/auth", a.AuthHandler)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,78 +102,132 @@ func incorrectPassword() (string, error) {
|
||||||
MAIN HANDLERS -- serve these through a server/router
|
MAIN HANDLERS -- serve these through a server/router
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// SignInHandler should be served at https://example.org/auth/sign_in.
|
// SignInGETHandler should be served at https://example.org/auth/sign_in.
|
||||||
|
// The idea is to present a sign in page to the user, where they can enter their username and password.
|
||||||
|
// The form will then POST to the sign in page, which will be handled by SignInPOSTHandler
|
||||||
|
func (a *API) SignInGETHandler(c *gin.Context) {
|
||||||
|
c.Data(http.StatusOK, "text/html; charset=utf-8", []byte(signInHTML))
|
||||||
|
}
|
||||||
|
|
||||||
|
// SignInPOSTHandler should be served at https://example.org/auth/sign_in.
|
||||||
// The idea is to present a sign in page to the user, where they can enter their username and password.
|
// The idea is to present a sign in page to the user, where they can enter their username and password.
|
||||||
// The handler will then redirect to the auth handler served at /auth
|
// The handler will then redirect to the auth handler served at /auth
|
||||||
func (a *API) SignInHandler(c *gin.Context) {
|
func (a *API) SignInPOSTHandler(c *gin.Context) {
|
||||||
s := sessions.Default(c)
|
s := sessions.Default(c)
|
||||||
if r.Method == "POST" {
|
form := &login{}
|
||||||
if r.Form == nil {
|
if err := c.ShouldBind(form); err != nil || form.Username == "" || form.Password == "" {
|
||||||
if err := r.ParseForm(); err != nil {
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
s.Set("username", r.Form.Get("username"))
|
|
||||||
s.Save()
|
|
||||||
|
|
||||||
w.Header().Set("Location", "/auth")
|
|
||||||
w.WriteHeader(http.StatusFound)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
http.ServeContent(w, r, "sign_in.html", time.Unix(0, 0), bytes.NewReader([]byte(signInHTML)))
|
s.Set("username", form.Username)
|
||||||
|
if err := s.Save(); err != nil {
|
||||||
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.Redirect(http.StatusFound, "/auth")
|
||||||
}
|
}
|
||||||
|
|
||||||
// TokenHandler should be served at https://example.org/oauth/token
|
// TokenHandler should be served at https://example.org/oauth/token
|
||||||
// The idea here is to serve an oauth access token to a user, which can be used for authorizing against non-public APIs.
|
// The idea here is to serve an oauth access token to a user, which can be used for authorizing against non-public APIs.
|
||||||
// See https://docs.joinmastodon.org/methods/apps/oauth/#obtain-a-token
|
// See https://docs.joinmastodon.org/methods/apps/oauth/#obtain-a-token
|
||||||
func (a *API) TokenHandler(w http.ResponseWriter, r *http.Request) {
|
func (a *API) TokenHandler(c *gin.Context) {
|
||||||
if err := a.server.HandleTokenRequest(w, r); err != nil {
|
if err := a.server.HandleTokenRequest(c.Writer, c.Request); err != nil {
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// AuthorizeHandler should be served at https://example.org/oauth/authorize
|
// AuthorizeHandler should be served as GET at https://example.org/oauth/authorize
|
||||||
// The idea here is to present an oauth authorize page to the user, with a button
|
// The idea here is to present an oauth authorize page to the user, with a button
|
||||||
// that they have to click to accept. See here: https://docs.joinmastodon.org/methods/apps/oauth/#authorize-a-user
|
// that they have to click to accept. See here: https://docs.joinmastodon.org/methods/apps/oauth/#authorize-a-user
|
||||||
func (a *API) AuthorizeHandler(w http.ResponseWriter, r *http.Request) {
|
func (a *API) AuthorizeHandler(c *gin.Context) {
|
||||||
store, err := session.Start(nil, w, r)
|
s := sessions.Default(c)
|
||||||
if err != nil {
|
form := &authorize{}
|
||||||
|
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
if err := c.ShouldBind(form); err != nil {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, ok := store.Get("username"); !ok {
|
if form.ResponseType == "" || form.ClientID == "" || form.RedirectURI == "" {
|
||||||
w.Header().Set("Location", "/auth/sign_in")
|
c.JSON(http.StatusBadRequest, gin.H{"error": "missing one of: response_type, client_id or redirect_uri"})
|
||||||
w.WriteHeader(http.StatusFound)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
http.ServeContent(w, r, "authorize.html", time.Unix(0, 0), bytes.NewReader([]byte(authorizeHTML)))
|
s.Set("force_login", form.ForceLogin)
|
||||||
|
s.Set("response_type", form.ResponseType)
|
||||||
|
s.Set("client_id", form.ClientID)
|
||||||
|
s.Set("redirect_uri", form.RedirectURI)
|
||||||
|
s.Set("scope", form.Scope)
|
||||||
|
if err := s.Save(); err != nil {
|
||||||
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||||
|
}
|
||||||
|
|
||||||
|
v := s.Get("username")
|
||||||
|
if username, ok := v.(string); !ok || username == "" {
|
||||||
|
c.Redirect(http.StatusFound, "/auth/sign_in")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Data(http.StatusOK, "text/html; charset=utf-8", []byte(authorizeHTML))
|
||||||
}
|
}
|
||||||
|
|
||||||
// AuthHandler should be served at https://example.org/auth
|
// AuthHandler should be served at https://example.org/auth
|
||||||
func (a *API) AuthHandler(w http.ResponseWriter, r *http.Request) {
|
func (a *API) AuthHandler(c *gin.Context) {
|
||||||
store, err := session.Start(r.Context(), w, r)
|
s := sessions.Default(c)
|
||||||
if err != nil {
|
|
||||||
a.log.Errorf("error creating session in authhandler: %s", err)
|
values := url.Values{}
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
||||||
|
if v, ok := s.Get("force_login").(string); !ok {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": "session missing force_login"})
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
values.Add("force_login", v)
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := s.Get("response_type").(string); !ok {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": "session missing response_type"})
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
values.Add("response_type", v)
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := s.Get("client_id").(string); !ok {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": "session missing client_id"})
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
values.Add("client_id", v)
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := s.Get("redirect_uri").(string); !ok {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": "session missing redirect_uri"})
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
values.Add("redirect_uri", v)
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := s.Get("scope").(string); !ok {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": "session missing scope"})
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
values.Add("scope", v)
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := s.Get("username").(string); !ok {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": "session missing username"})
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
values.Add("username", v)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Request.Form = values
|
||||||
|
|
||||||
|
if err := s.Save(); err != nil {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var form url.Values
|
if err := a.server.HandleAuthorizeRequest(c.Writer, c.Request); err != nil {
|
||||||
if v, ok := store.Get("ReturnUri"); ok {
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||||
form = v.(url.Values)
|
|
||||||
}
|
|
||||||
r.Form = form
|
|
||||||
|
|
||||||
store.Delete("ReturnUri")
|
|
||||||
store.Save()
|
|
||||||
|
|
||||||
if err := a.server.HandleAuthorizeRequest(w, r); err != nil {
|
|
||||||
a.log.Errorf("error in authhandler during handleauthorizerequest: %s", err)
|
|
||||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -203,34 +267,14 @@ func (a *API) PasswordAuthorizationHandler(email string, password string) (useri
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// UserAuthorizationHandler gets the user's email address from the session key 'username'
|
// UserAuthorizationHandler gets the user's email address from the form key 'username'
|
||||||
// or redirects to the /auth/sign_in page, if this key is not present.
|
// or redirects to the /auth/sign_in page, if this key is not present.
|
||||||
func (a *API) UserAuthorizationHandler(w http.ResponseWriter, r *http.Request) (string, error) {
|
func (a *API) UserAuthorizationHandler(w http.ResponseWriter, r *http.Request) (string, error) {
|
||||||
|
|
||||||
a.log.Errorf("entering userauthorizationhandler")
|
username := r.FormValue("username")
|
||||||
|
if username == "" {
|
||||||
sessionStore, err := session.Start(r.Context(), w, r)
|
http.Redirect(w, r, "/auth/sign_in", http.StatusFound)
|
||||||
if err != nil {
|
return "", nil
|
||||||
a.log.Errorf("error starting session: %s", err)
|
|
||||||
return "", err
|
|
||||||
}
|
}
|
||||||
|
return username, nil
|
||||||
v, ok := sessionStore.Get("username")
|
|
||||||
if !ok {
|
|
||||||
if err := r.ParseForm(); err != nil {
|
|
||||||
a.log.Errorf("error parsing form: %s", err)
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
sessionStore.Set("ReturnUri", r.Form)
|
|
||||||
sessionStore.Save()
|
|
||||||
|
|
||||||
w.Header().Set("Location", "/auth/sign_in")
|
|
||||||
w.WriteHeader(http.StatusFound)
|
|
||||||
return v.(string), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
sessionStore.Delete("username")
|
|
||||||
sessionStore.Save()
|
|
||||||
return v.(string), nil
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
|
|
||||||
"github.com/go-pg/pg/v10"
|
"github.com/go-pg/pg/v10"
|
||||||
"github.com/go-pg/pg/v10/orm"
|
"github.com/go-pg/pg/v10/orm"
|
||||||
|
"github.com/google/uuid"
|
||||||
"github.com/gotosocial/gotosocial/internal/api"
|
"github.com/gotosocial/gotosocial/internal/api"
|
||||||
"github.com/gotosocial/gotosocial/internal/config"
|
"github.com/gotosocial/gotosocial/internal/config"
|
||||||
"github.com/gotosocial/gotosocial/internal/gtsmodel"
|
"github.com/gotosocial/gotosocial/internal/gtsmodel"
|
||||||
|
@ -18,35 +19,37 @@ import (
|
||||||
|
|
||||||
type OauthTestSuite struct {
|
type OauthTestSuite struct {
|
||||||
suite.Suite
|
suite.Suite
|
||||||
tokenStore oauth2.TokenStore
|
tokenStore oauth2.TokenStore
|
||||||
clientStore oauth2.ClientStore
|
clientStore oauth2.ClientStore
|
||||||
conn *pg.DB
|
conn *pg.DB
|
||||||
testClientID string
|
testUser *gtsmodel.User
|
||||||
testClientSecret string
|
testClient *oauthClient
|
||||||
testClientDomain string
|
config *config.Config
|
||||||
testClientUserID string
|
|
||||||
testUser *gtsmodel.User
|
|
||||||
config *config.Config
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const ()
|
const ()
|
||||||
|
|
||||||
// SetupSuite sets some variables on the suite that we can use as consts (more or less) throughout
|
// SetupSuite sets some variables on the suite that we can use as consts (more or less) throughout
|
||||||
func (suite *OauthTestSuite) SetupSuite() {
|
func (suite *OauthTestSuite) SetupSuite() {
|
||||||
suite.testClientID = "test-client-id"
|
|
||||||
suite.testClientSecret = "test-client-secret"
|
|
||||||
suite.testClientDomain = "https://example.org"
|
|
||||||
suite.testClientUserID = "test-client-user-id"
|
|
||||||
encryptedPassword, err := bcrypt.GenerateFromPassword([]byte("test-password"), bcrypt.DefaultCost)
|
encryptedPassword, err := bcrypt.GenerateFromPassword([]byte("test-password"), bcrypt.DefaultCost)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Panicf("error encrypting user pass: %s", err)
|
logrus.Panicf("error encrypting user pass: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
userID := uuid.NewString()
|
||||||
suite.testUser = >smodel.User{
|
suite.testUser = >smodel.User{
|
||||||
|
ID: userID,
|
||||||
EncryptedPassword: string(encryptedPassword),
|
EncryptedPassword: string(encryptedPassword),
|
||||||
Email: "user@example.org",
|
Email: "user@localhost",
|
||||||
CreatedAt: time.Now(),
|
CreatedAt: time.Now(),
|
||||||
UpdatedAt: time.Now(),
|
UpdatedAt: time.Now(),
|
||||||
AccountID: "whatever",
|
AccountID: "some-account-id-it-doesn't-matter-really",
|
||||||
|
}
|
||||||
|
suite.testClient = &oauthClient{
|
||||||
|
ID: "a-known-client-id",
|
||||||
|
Secret: "some-secret",
|
||||||
|
Domain: "localhost",
|
||||||
|
UserID: userID,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,6 +81,10 @@ func (suite *OauthTestSuite) SetupTest() {
|
||||||
logrus.Panicf("could not insert test user into db: %s", err)
|
logrus.Panicf("could not insert test user into db: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if _, err := suite.conn.Model(suite.testClient).Insert(); err != nil {
|
||||||
|
logrus.Panicf("could not insert test client into db: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TearDownTest drops the oauth_clients table and closes the pg connection after each test
|
// TearDownTest drops the oauth_clients table and closes the pg connection after each test
|
||||||
|
@ -107,7 +114,7 @@ func (suite *OauthTestSuite) TestAPIInitialize() {
|
||||||
api.AddRoutes(r)
|
api.AddRoutes(r)
|
||||||
go r.Start()
|
go r.Start()
|
||||||
time.Sleep(30 * time.Second)
|
time.Sleep(30 * time.Second)
|
||||||
// http://localhost:8080/oauth/authorize?client_id=whatever
|
// http://localhost:8080/oauth/authorize?client_id=a-known-client-id&redirect_uri=urn:ietf:wg:oauth:2.0:oob&response_type=code
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestOauthTestSuite(t *testing.T) {
|
func TestOauthTestSuite(t *testing.T) {
|
||||||
|
|
|
@ -20,6 +20,7 @@ package oauth
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/go-pg/pg/v10"
|
"github.com/go-pg/pg/v10"
|
||||||
"github.com/gotosocial/oauth2/v4"
|
"github.com/gotosocial/oauth2/v4"
|
||||||
|
@ -42,7 +43,7 @@ func (pcs *pgClientStore) GetByID(ctx context.Context, clientID string) (oauth2.
|
||||||
ID: clientID,
|
ID: clientID,
|
||||||
}
|
}
|
||||||
if err := pcs.conn.WithContext(ctx).Model(poc).Where("id = ?", poc.ID).Select(); err != nil {
|
if err := pcs.conn.WithContext(ctx).Model(poc).Where("id = ?", poc.ID).Select(); err != nil {
|
||||||
return nil, err
|
return nil, fmt.Errorf("error in clientstore getbyid searching for client %s: %s", clientID, err)
|
||||||
}
|
}
|
||||||
return models.New(poc.ID, poc.Secret, poc.Domain, poc.UserID), nil
|
return models.New(poc.ID, poc.Secret, poc.Domain, poc.UserID), nil
|
||||||
}
|
}
|
||||||
|
@ -55,7 +56,10 @@ func (pcs *pgClientStore) Set(ctx context.Context, id string, cli oauth2.ClientI
|
||||||
UserID: cli.GetUserID(),
|
UserID: cli.GetUserID(),
|
||||||
}
|
}
|
||||||
_, err := pcs.conn.WithContext(ctx).Model(poc).OnConflict("(id) DO UPDATE").Insert()
|
_, err := pcs.conn.WithContext(ctx).Model(poc).OnConflict("(id) DO UPDATE").Insert()
|
||||||
return err
|
if err != nil {
|
||||||
|
return fmt.Errorf("error in clientstore set: %s", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pcs *pgClientStore) Delete(ctx context.Context, id string) error {
|
func (pcs *pgClientStore) Delete(ctx context.Context, id string) error {
|
||||||
|
@ -63,7 +67,10 @@ func (pcs *pgClientStore) Delete(ctx context.Context, id string) error {
|
||||||
ID: id,
|
ID: id,
|
||||||
}
|
}
|
||||||
_, err := pcs.conn.WithContext(ctx).Model(poc).Where("id = ?", poc.ID).Delete()
|
_, err := pcs.conn.WithContext(ctx).Model(poc).Where("id = ?", poc.ID).Delete()
|
||||||
return err
|
if err != nil {
|
||||||
|
return fmt.Errorf("error in clientstore delete: %s", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type oauthClient struct {
|
type oauthClient struct {
|
||||||
|
|
|
@ -96,7 +96,6 @@ func (suite *PgClientStoreTestSuite) TestClientSetAndDelete() {
|
||||||
deletedClient, err := cs.GetByID(context.Background(), suite.testClientID)
|
deletedClient, err := cs.GetByID(context.Background(), suite.testClientID)
|
||||||
suite.Assert().Nil(deletedClient)
|
suite.Assert().Nil(deletedClient)
|
||||||
suite.Assert().NotNil(err)
|
suite.Assert().NotNil(err)
|
||||||
suite.EqualValues("pg: no rows in result set", err.Error())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPgClientStoreTestSuite(t *testing.T) {
|
func TestPgClientStoreTestSuite(t *testing.T) {
|
||||||
|
|
|
@ -21,6 +21,7 @@ package oauth
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/go-pg/pg/v10"
|
"github.com/go-pg/pg/v10"
|
||||||
|
@ -98,32 +99,44 @@ func (pts *pgTokenStore) Create(ctx context.Context, info oauth2.TokenInfo) erro
|
||||||
return errors.New("info param was not a models.Token")
|
return errors.New("info param was not a models.Token")
|
||||||
}
|
}
|
||||||
_, err := pts.conn.WithContext(ctx).Model(oauthTokenToPGToken(t)).Insert()
|
_, err := pts.conn.WithContext(ctx).Model(oauthTokenToPGToken(t)).Insert()
|
||||||
return err
|
if err != nil {
|
||||||
|
return fmt.Errorf("error in tokenstore create: %s", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoveByCode deletes a token from the DB based on the Code field
|
// RemoveByCode deletes a token from the DB based on the Code field
|
||||||
func (pts *pgTokenStore) RemoveByCode(ctx context.Context, code string) error {
|
func (pts *pgTokenStore) RemoveByCode(ctx context.Context, code string) error {
|
||||||
_, err := pts.conn.Model(&oauthToken{}).Where("code = ?", code).Delete()
|
_, err := pts.conn.Model(&oauthToken{}).Where("code = ?", code).Delete()
|
||||||
return err
|
if err != nil {
|
||||||
|
return fmt.Errorf("error in tokenstore removebycode: %s", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoveByAccess deletes a token from the DB based on the Access field
|
// RemoveByAccess deletes a token from the DB based on the Access field
|
||||||
func (pts *pgTokenStore) RemoveByAccess(ctx context.Context, access string) error {
|
func (pts *pgTokenStore) RemoveByAccess(ctx context.Context, access string) error {
|
||||||
_, err := pts.conn.Model(&oauthToken{}).Where("access = ?", access).Delete()
|
_, err := pts.conn.Model(&oauthToken{}).Where("access = ?", access).Delete()
|
||||||
return err
|
if err != nil {
|
||||||
|
return fmt.Errorf("error in tokenstore removebyaccess: %s", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoveByRefresh deletes a token from the DB based on the Refresh field
|
// RemoveByRefresh deletes a token from the DB based on the Refresh field
|
||||||
func (pts *pgTokenStore) RemoveByRefresh(ctx context.Context, refresh string) error {
|
func (pts *pgTokenStore) RemoveByRefresh(ctx context.Context, refresh string) error {
|
||||||
_, err := pts.conn.Model(&oauthToken{}).Where("refresh = ?", refresh).Delete()
|
_, err := pts.conn.Model(&oauthToken{}).Where("refresh = ?", refresh).Delete()
|
||||||
return err
|
if err != nil {
|
||||||
|
return fmt.Errorf("error in tokenstore removebyrefresh: %s", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetByCode selects a token from the DB based on the Code field
|
// GetByCode selects a token from the DB based on the Code field
|
||||||
func (pts *pgTokenStore) GetByCode(ctx context.Context, code string) (oauth2.TokenInfo, error) {
|
func (pts *pgTokenStore) GetByCode(ctx context.Context, code string) (oauth2.TokenInfo, error) {
|
||||||
pgt := &oauthToken{}
|
pgt := &oauthToken{}
|
||||||
if err := pts.conn.Model(pgt).Where("code = ?", code).Select(); err != nil {
|
if err := pts.conn.Model(pgt).Where("code = ?", code).Select(); err != nil {
|
||||||
return nil, err
|
return nil, fmt.Errorf("error in tokenstore getbycode: %s", err)
|
||||||
}
|
}
|
||||||
return pgTokenToOauthToken(pgt), nil
|
return pgTokenToOauthToken(pgt), nil
|
||||||
}
|
}
|
||||||
|
@ -132,7 +145,7 @@ func (pts *pgTokenStore) GetByCode(ctx context.Context, code string) (oauth2.Tok
|
||||||
func (pts *pgTokenStore) GetByAccess(ctx context.Context, access string) (oauth2.TokenInfo, error) {
|
func (pts *pgTokenStore) GetByAccess(ctx context.Context, access string) (oauth2.TokenInfo, error) {
|
||||||
pgt := &oauthToken{}
|
pgt := &oauthToken{}
|
||||||
if err := pts.conn.Model(pgt).Where("access = ?", access).Select(); err != nil {
|
if err := pts.conn.Model(pgt).Where("access = ?", access).Select(); err != nil {
|
||||||
return nil, err
|
return nil, fmt.Errorf("error in tokenstore getbyaccess: %s", err)
|
||||||
}
|
}
|
||||||
return pgTokenToOauthToken(pgt), nil
|
return pgTokenToOauthToken(pgt), nil
|
||||||
}
|
}
|
||||||
|
@ -141,7 +154,7 @@ func (pts *pgTokenStore) GetByAccess(ctx context.Context, access string) (oauth2
|
||||||
func (pts *pgTokenStore) GetByRefresh(ctx context.Context, refresh string) (oauth2.TokenInfo, error) {
|
func (pts *pgTokenStore) GetByRefresh(ctx context.Context, refresh string) (oauth2.TokenInfo, error) {
|
||||||
pgt := &oauthToken{}
|
pgt := &oauthToken{}
|
||||||
if err := pts.conn.Model(pgt).Where("refresh = ?", refresh).Select(); err != nil {
|
if err := pts.conn.Model(pgt).Where("refresh = ?", refresh).Select(); err != nil {
|
||||||
return nil, err
|
return nil, fmt.Errorf("error in tokenstore getbyrefresh: %s", err)
|
||||||
}
|
}
|
||||||
return pgTokenToOauthToken(pgt), nil
|
return pgTokenToOauthToken(pgt), nil
|
||||||
}
|
}
|
||||||
|
@ -165,15 +178,15 @@ type oauthToken struct {
|
||||||
UserID string
|
UserID string
|
||||||
RedirectURI string
|
RedirectURI string
|
||||||
Scope string
|
Scope string
|
||||||
Code string `pg:",pk"`
|
Code string `pg:"default:'',pk"`
|
||||||
CodeChallenge string
|
CodeChallenge string
|
||||||
CodeChallengeMethod string
|
CodeChallengeMethod string
|
||||||
CodeCreateAt time.Time `pg:"type:timestamp"`
|
CodeCreateAt time.Time `pg:"type:timestamp"`
|
||||||
CodeExpiresAt time.Time `pg:"type:timestamp"`
|
CodeExpiresAt time.Time `pg:"type:timestamp"`
|
||||||
Access string `pg:",pk"`
|
Access string `pg:"default:'',pk"`
|
||||||
AccessCreateAt time.Time `pg:"type:timestamp"`
|
AccessCreateAt time.Time `pg:"type:timestamp"`
|
||||||
AccessExpiresAt time.Time `pg:"type:timestamp"`
|
AccessExpiresAt time.Time `pg:"type:timestamp"`
|
||||||
Refresh string `pg:",pk"`
|
Refresh string `pg:"default:'',pk"`
|
||||||
RefreshCreateAt time.Time `pg:"type:timestamp"`
|
RefreshCreateAt time.Time `pg:"type:timestamp"`
|
||||||
RefreshExpiresAt time.Time `pg:"type:timestamp"`
|
RefreshExpiresAt time.Time `pg:"type:timestamp"`
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue