implement database user login/auth
This commit is contained in:
parent
40f717d683
commit
d4043ebc88
|
@ -3,14 +3,27 @@ package controllers
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
"git.jamesravey.me/ravenscroftj/indiescrobble/models"
|
||||||
"git.jamesravey.me/ravenscroftj/indiescrobble/scrobble"
|
"git.jamesravey.me/ravenscroftj/indiescrobble/scrobble"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Index(c *gin.Context) {
|
func Index(c *gin.Context) {
|
||||||
|
|
||||||
|
// this is an authed endpoint so 'user' must be set and if not panicking is fair
|
||||||
|
currentUser, exists := c.Get("user")
|
||||||
|
|
||||||
|
var user *models.BaseUser
|
||||||
|
|
||||||
|
if exists {
|
||||||
|
user = currentUser.(*models.BaseUser)
|
||||||
|
}else{
|
||||||
|
user = nil
|
||||||
|
}
|
||||||
|
|
||||||
c.HTML(http.StatusOK, "index.tmpl", gin.H{
|
c.HTML(http.StatusOK, "index.tmpl", gin.H{
|
||||||
"title": "test",
|
"title": "test",
|
||||||
"user": c.GetString("user"),
|
"user": user,
|
||||||
"scrobbleTypes": scrobble.ScrobbleTypeNames,
|
"scrobbleTypes": scrobble.ScrobbleTypeNames,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,21 +15,22 @@ import (
|
||||||
"github.com/go-chi/jwtauth/v5"
|
"github.com/go-chi/jwtauth/v5"
|
||||||
"github.com/hacdias/indieauth"
|
"github.com/hacdias/indieauth"
|
||||||
"github.com/lestrrat-go/jwx/jwt"
|
"github.com/lestrrat-go/jwx/jwt"
|
||||||
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
type IndieAuthManager struct {
|
type IndieAuthManager struct {
|
||||||
iac *indieauth.Client
|
iac *indieauth.Client
|
||||||
jwtAuth *jwtauth.JWTAuth
|
jwtAuth *jwtauth.JWTAuth
|
||||||
|
db *gorm.DB
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewIndieAuthManager() *IndieAuthManager{
|
func NewIndieAuthManager(db *gorm.DB) *IndieAuthManager {
|
||||||
|
|
||||||
config := config.GetConfig()
|
config := config.GetConfig()
|
||||||
|
|
||||||
iam := new(IndieAuthManager)
|
iam := new(IndieAuthManager)
|
||||||
iam.iac = indieauth.NewClient(config.GetString("indieauth.clientName"), config.GetString("indieauth.redirectURL"), nil)
|
iam.iac = indieauth.NewClient(config.GetString("indieauth.clientName"), config.GetString("indieauth.redirectURL"), nil)
|
||||||
|
iam.db = db
|
||||||
|
|
||||||
iam.jwtAuth = jwtauth.New("HS256", []byte(config.GetString("jwt.signKey")), []byte(config.GetString("jwt.signKey")))
|
iam.jwtAuth = jwtauth.New("HS256", []byte(config.GetString("jwt.signKey")), []byte(config.GetString("jwt.signKey")))
|
||||||
|
|
||||||
return iam
|
return iam
|
||||||
|
@ -61,7 +62,32 @@ func (iam *IndieAuthManager) GetCurrentUser(c *gin.Context) *models.BaseUser {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
user := models.BaseUser{Me: me.(string), Token: indietok.(string)}
|
// see if the user exists in the database or set up their profile
|
||||||
|
userRecord := models.User{}
|
||||||
|
result := iam.db.First(&userRecord, models.User{Me: me.(string)})
|
||||||
|
|
||||||
|
if result.Error != nil {
|
||||||
|
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
|
||||||
|
log.Printf("Create new user profile for user %v\n", me)
|
||||||
|
|
||||||
|
// create user record for current user
|
||||||
|
userRecord = models.User{Me: me.(string)}
|
||||||
|
userRecord.GenerateRandomKey()
|
||||||
|
result := iam.db.Create(&userRecord)
|
||||||
|
|
||||||
|
if result.Error != nil {
|
||||||
|
log.Printf("Failed to create user record in db: %v", result.Error)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
log.Printf("Failed to get user from db: %v\n", result.Error)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
user := models.BaseUser{Me: me.(string), Token: indietok.(string), UserRecord: &userRecord}
|
||||||
|
|
||||||
return &user
|
return &user
|
||||||
|
|
||||||
|
@ -71,7 +97,6 @@ func (iam *IndieAuthManager) GetCurrentUser(c *gin.Context) *models.BaseUser {
|
||||||
|
|
||||||
func (iam *IndieAuthManager) getInformation(c *gin.Context) (*indieauth.AuthInfo, string, error) {
|
func (iam *IndieAuthManager) getInformation(c *gin.Context) (*indieauth.AuthInfo, string, error) {
|
||||||
|
|
||||||
|
|
||||||
config := config.GetConfig()
|
config := config.GetConfig()
|
||||||
|
|
||||||
cookie, err := c.Request.Cookie(config.GetString("indieauth.oauthCookieName"))
|
cookie, err := c.Request.Cookie(config.GetString("indieauth.oauthCookieName"))
|
||||||
|
@ -166,7 +191,6 @@ func (iam *IndieAuthManager) saveAuthInfo(w http.ResponseWriter, r *http.Request
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func (iam *IndieAuthManager) Logout(c *gin.Context) {
|
func (iam *IndieAuthManager) Logout(c *gin.Context) {
|
||||||
|
|
||||||
// delete the cookie
|
// delete the cookie
|
||||||
|
@ -227,7 +251,6 @@ func (iam *IndieAuthManager) IndieAuthLoginPost(c *gin.Context) {
|
||||||
|
|
||||||
fmt.Printf("profile: %v\n", i)
|
fmt.Printf("profile: %v\n", i)
|
||||||
|
|
||||||
|
|
||||||
err = iam.saveAuthInfo(c.Writer, c.Request, i)
|
err = iam.saveAuthInfo(c.Writer, c.Request, i)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.HTML(http.StatusBadRequest, "error.tmpl", gin.H{
|
c.HTML(http.StatusBadRequest, "error.tmpl", gin.H{
|
||||||
|
@ -242,7 +265,6 @@ func (iam *IndieAuthManager) IndieAuthLoginPost(c *gin.Context) {
|
||||||
c.Redirect(http.StatusSeeOther, redirect)
|
c.Redirect(http.StatusSeeOther, redirect)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func (iam *IndieAuthManager) LoginCallbackGet(c *gin.Context) {
|
func (iam *IndieAuthManager) LoginCallbackGet(c *gin.Context) {
|
||||||
|
|
||||||
config := config.GetConfig()
|
config := config.GetConfig()
|
||||||
|
@ -263,7 +285,6 @@ func (iam *IndieAuthManager) LoginCallbackGet(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// profile, err := iam.iac.FetchProfile(i, code)
|
// profile, err := iam.iac.FetchProfile(i, code)
|
||||||
// if err != nil {
|
// if err != nil {
|
||||||
// c.HTML(http.StatusBadRequest, "error.tmpl", gin.H{
|
// c.HTML(http.StatusBadRequest, "error.tmpl", gin.H{
|
||||||
|
@ -272,7 +293,6 @@ func (iam *IndieAuthManager) LoginCallbackGet(c *gin.Context) {
|
||||||
// return
|
// return
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
|
||||||
token, _, err := iam.iac.GetToken(i, code)
|
token, _, err := iam.iac.GetToken(i, code)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.HTML(http.StatusBadRequest, "error.tmpl", gin.H{
|
c.HTML(http.StatusBadRequest, "error.tmpl", gin.H{
|
||||||
|
@ -283,7 +303,6 @@ func (iam *IndieAuthManager) LoginCallbackGet(c *gin.Context) {
|
||||||
|
|
||||||
me := token.Extra("me").(string)
|
me := token.Extra("me").(string)
|
||||||
|
|
||||||
|
|
||||||
if err := indieauth.IsValidProfileURL(me); err != nil {
|
if err := indieauth.IsValidProfileURL(me); err != nil {
|
||||||
err = fmt.Errorf("invalid 'me': %w", err)
|
err = fmt.Errorf("invalid 'me': %w", err)
|
||||||
c.HTML(http.StatusBadRequest, "error.tmpl", gin.H{
|
c.HTML(http.StatusBadRequest, "error.tmpl", gin.H{
|
||||||
|
|
|
@ -99,7 +99,14 @@ func PreviewScrobble(c *gin.Context){
|
||||||
|
|
||||||
discovery := micropub.MicropubDiscoveryService{}
|
discovery := micropub.MicropubDiscoveryService{}
|
||||||
|
|
||||||
discovery.Discover(currentUser.Me, currentUser.Token )
|
config, err := discovery.Discover(currentUser.Me, currentUser.Token )
|
||||||
|
|
||||||
|
if err != nil{
|
||||||
|
c.HTML(http.StatusBadRequest, "error.tmpl", gin.H{
|
||||||
|
"message": err,
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
c.HTML(http.StatusOK, "preview.tmpl", gin.H{
|
c.HTML(http.StatusOK, "preview.tmpl", gin.H{
|
||||||
"user": currentUser,
|
"user": currentUser,
|
||||||
|
@ -109,6 +116,7 @@ func PreviewScrobble(c *gin.Context){
|
||||||
"when": c.Request.Form.Get("when"),
|
"when": c.Request.Form.Get("when"),
|
||||||
"rating": c.Request.Form.Get("rating"),
|
"rating": c.Request.Form.Get("rating"),
|
||||||
"content": c.Request.Form.Get("content"),
|
"content": c.Request.Form.Get("content"),
|
||||||
|
"config": config,
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
|
@ -7,12 +7,10 @@ import (
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
func AuthMiddleware(requireValidUser bool) gin.HandlerFunc {
|
func AuthMiddleware(requireValidUser bool, iam *controllers.IndieAuthManager) gin.HandlerFunc {
|
||||||
return func(c *gin.Context) {
|
return func(c *gin.Context) {
|
||||||
// config := config.GetConfig()
|
// config := config.GetConfig()
|
||||||
|
|
||||||
iam := controllers.NewIndieAuthManager()
|
|
||||||
|
|
||||||
currentUser := iam.GetCurrentUser(c)
|
currentUser := iam.GetCurrentUser(c)
|
||||||
|
|
||||||
if requireValidUser && (currentUser == nil) {
|
if requireValidUser && (currentUser == nil) {
|
||||||
|
@ -40,4 +38,3 @@ func AuthMiddleware(requireValidUser bool) gin.HandlerFunc {
|
||||||
c.Next()
|
c.Next()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,9 +5,10 @@ import (
|
||||||
"git.jamesravey.me/ravenscroftj/indiescrobble/controllers"
|
"git.jamesravey.me/ravenscroftj/indiescrobble/controllers"
|
||||||
"git.jamesravey.me/ravenscroftj/indiescrobble/middlewares"
|
"git.jamesravey.me/ravenscroftj/indiescrobble/middlewares"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewRouter() *gin.Engine {
|
func NewRouter(db *gorm.DB) *gin.Engine {
|
||||||
router := gin.New()
|
router := gin.New()
|
||||||
router.Use(gin.Logger())
|
router.Use(gin.Logger())
|
||||||
router.Use(gin.Recovery())
|
router.Use(gin.Recovery())
|
||||||
|
@ -16,12 +17,11 @@ func NewRouter() *gin.Engine {
|
||||||
|
|
||||||
health := new(controllers.HealthController)
|
health := new(controllers.HealthController)
|
||||||
|
|
||||||
iam := controllers.NewIndieAuthManager()
|
iam := controllers.NewIndieAuthManager(db)
|
||||||
|
|
||||||
|
|
||||||
router.GET("/health", health.Status)
|
router.GET("/health", health.Status)
|
||||||
|
|
||||||
router.Use(middlewares.AuthMiddleware(false))
|
router.Use(middlewares.AuthMiddleware(false, iam))
|
||||||
|
|
||||||
router.GET("/", controllers.Index)
|
router.GET("/", controllers.Index)
|
||||||
|
|
||||||
|
@ -33,14 +33,12 @@ func NewRouter() *gin.Engine {
|
||||||
router.GET("/auth", iam.LoginCallbackGet)
|
router.GET("/auth", iam.LoginCallbackGet)
|
||||||
router.GET("/logout", iam.Logout)
|
router.GET("/logout", iam.Logout)
|
||||||
|
|
||||||
authed := router.Use(middlewares.AuthMiddleware(true))
|
authed := router.Use(middlewares.AuthMiddleware(true, iam))
|
||||||
|
|
||||||
// add scrobble endpoints
|
// add scrobble endpoints
|
||||||
authed.GET("/scrobble", controllers.Scrobble)
|
authed.GET("/scrobble", controllers.Scrobble)
|
||||||
authed.POST("/scrobble/preview", controllers.PreviewScrobble)
|
authed.POST("/scrobble/preview", controllers.PreviewScrobble)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// v1 := router.Group("v1")
|
// v1 := router.Group("v1")
|
||||||
// {
|
// {
|
||||||
// userGroup := v1.Group("user")
|
// userGroup := v1.Group("user")
|
||||||
|
|
|
@ -15,7 +15,6 @@ import (
|
||||||
func Init() {
|
func Init() {
|
||||||
config := config.GetConfig()
|
config := config.GetConfig()
|
||||||
|
|
||||||
|
|
||||||
var dialect gorm.Dialector
|
var dialect gorm.Dialector
|
||||||
|
|
||||||
if config.GetString("server.database.driver") == "sqlite" {
|
if config.GetString("server.database.driver") == "sqlite" {
|
||||||
|
@ -32,8 +31,7 @@ func Init() {
|
||||||
|
|
||||||
db.AutoMigrate(&models.User{})
|
db.AutoMigrate(&models.User{})
|
||||||
|
|
||||||
|
r := NewRouter(db)
|
||||||
r := NewRouter()
|
|
||||||
r.LoadHTMLGlob("templates/*.tmpl")
|
r.LoadHTMLGlob("templates/*.tmpl")
|
||||||
r.Run(fmt.Sprintf("%v:%v", config.GetString("server.host"), config.GetString("server.port")))
|
r.Run(fmt.Sprintf("%v:%v", config.GetString("server.host"), config.GetString("server.port")))
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
{{ define "footer.tmpl" }}
|
||||||
|
<footer>
|
||||||
|
IndieScrobble is a FLOSS service provided by <a href="https://brainsteam.co.uk">James Ravenscroft</a>. It is licensed under AGPL-3.0.
|
||||||
|
</footer>
|
||||||
|
{{end}}
|
|
@ -6,7 +6,7 @@
|
||||||
<main>
|
<main>
|
||||||
|
|
||||||
{{ if .user }}
|
{{ if .user }}
|
||||||
Logged in as {{.user}} <a href="/logout"><button>Log Out</button></a>
|
Logged in as {{.user.Me}} <a href="/logout"><button>Log Out</button></a>
|
||||||
|
|
||||||
<h2>Add A Scrobble</h2>
|
<h2>Add A Scrobble</h2>
|
||||||
|
|
||||||
|
@ -32,5 +32,6 @@
|
||||||
</form>
|
</form>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
</main>
|
</main>
|
||||||
|
{{ template "footer.tmpl" . }}
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
|
@ -9,7 +9,7 @@
|
||||||
{{ $scrobbleType := .scrobbleType }}
|
{{ $scrobbleType := .scrobbleType }}
|
||||||
|
|
||||||
{{ if .user }}
|
{{ if .user }}
|
||||||
Logged in as {{.user}} <a href="/logout"><button>Log Out</button></a>
|
Logged in as {{.user.Me}} <a href="/logout"><button>Log Out</button></a>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
|
|
||||||
|
@ -38,6 +38,13 @@
|
||||||
<pre>{{.content}}</pre>
|
<pre>{{.content}}</pre>
|
||||||
<input type="hidden" name="content" value="{{.content}}"/>
|
<input type="hidden" name="content" value="{{.content}}"/>
|
||||||
|
|
||||||
|
{{if .config.SyndicateTargets}}
|
||||||
|
<h3>Syndication Options</h3>
|
||||||
|
{{ range $target := .config.SyndicateTargets}}
|
||||||
|
<label><input type="checkbox" name="mp-syndicate[]" value="{{$target.Uid}}" /> {{$target.Name}}</label><br />
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
|
||||||
<br/>
|
<br/>
|
||||||
<button type="submit">Submit Post > ></button>
|
<button type="submit">Submit Post > ></button>
|
||||||
|
|
||||||
|
@ -49,6 +56,7 @@
|
||||||
|
|
||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
{{ template "footer.tmpl" . }}
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
{{end}}
|
{{end}}
|
|
@ -80,10 +80,8 @@
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
{{ template "footer.tmpl" . }}
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
{{end}}
|
{{end}}
|
Loading…
Reference in New Issue