mirror of
https://github.com/pushbits/server.git
synced 2025-04-28 17:56:50 +02:00
107 lines
2.8 KiB
Go
107 lines
2.8 KiB
Go
// Package authentication provides definitions and functionality related to user authentication.
|
|
package authentication
|
|
|
|
import (
|
|
"errors"
|
|
"net/http"
|
|
|
|
"github.com/pushbits/server/internal/authentication/credentials"
|
|
"github.com/pushbits/server/internal/model"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
const (
|
|
headerName = "X-Gotify-Key"
|
|
)
|
|
|
|
// The Database interface for encapsulating database access.
|
|
type Database interface {
|
|
GetApplicationByToken(token string) (*model.Application, error)
|
|
GetUserByName(name string) (*model.User, error)
|
|
}
|
|
|
|
// Authenticator is the provider for authentication middleware.
|
|
type Authenticator struct {
|
|
DB Database
|
|
}
|
|
|
|
type hasUserProperty func(user *model.User) bool
|
|
|
|
func (a *Authenticator) userFromBasicAuth(ctx *gin.Context) (*model.User, error) {
|
|
if name, password, ok := ctx.Request.BasicAuth(); ok {
|
|
if user, err := a.DB.GetUserByName(name); err != nil {
|
|
return nil, err
|
|
} else if user != nil && credentials.ComparePassword(user.PasswordHash, []byte(password)) {
|
|
return user, nil
|
|
}
|
|
|
|
return nil, errors.New("credentials were invalid")
|
|
}
|
|
|
|
return nil, errors.New("no credentials were supplied")
|
|
}
|
|
|
|
func (a *Authenticator) requireUserProperty(has hasUserProperty) gin.HandlerFunc {
|
|
return func(ctx *gin.Context) {
|
|
user, err := a.userFromBasicAuth(ctx)
|
|
if err != nil {
|
|
ctx.AbortWithError(http.StatusForbidden, err)
|
|
return
|
|
}
|
|
|
|
if !has(user) {
|
|
ctx.AbortWithError(http.StatusForbidden, errors.New("authentication failed"))
|
|
return
|
|
}
|
|
|
|
ctx.Set("user", user)
|
|
}
|
|
}
|
|
|
|
// RequireUser returns a Gin middleware which requires valid user credentials to be supplied with the request.
|
|
func (a *Authenticator) RequireUser() gin.HandlerFunc {
|
|
return a.requireUserProperty(func(_ *model.User) bool {
|
|
return true
|
|
})
|
|
}
|
|
|
|
// RequireAdmin returns a Gin middleware which requires valid admin credentials to be supplied with the request.
|
|
func (a *Authenticator) RequireAdmin() gin.HandlerFunc {
|
|
return a.requireUserProperty(func(user *model.User) bool {
|
|
return user.IsAdmin
|
|
})
|
|
}
|
|
|
|
func (a *Authenticator) tokenFromQueryOrHeader(ctx *gin.Context) string {
|
|
if token := a.tokenFromQuery(ctx); token != "" {
|
|
return token
|
|
} else if token := a.tokenFromHeader(ctx); token != "" {
|
|
return token
|
|
}
|
|
|
|
return ""
|
|
}
|
|
|
|
func (a *Authenticator) tokenFromQuery(ctx *gin.Context) string {
|
|
return ctx.Request.URL.Query().Get("token")
|
|
}
|
|
|
|
func (a *Authenticator) tokenFromHeader(ctx *gin.Context) string {
|
|
return ctx.Request.Header.Get(headerName)
|
|
}
|
|
|
|
// RequireApplicationToken returns a Gin middleware which requires an application token to be supplied with the request.
|
|
func (a *Authenticator) RequireApplicationToken() gin.HandlerFunc {
|
|
return func(ctx *gin.Context) {
|
|
token := a.tokenFromQueryOrHeader(ctx)
|
|
|
|
app, err := a.DB.GetApplicationByToken(token)
|
|
if err != nil {
|
|
ctx.AbortWithError(http.StatusForbidden, err)
|
|
return
|
|
}
|
|
|
|
ctx.Set("app", app)
|
|
}
|
|
}
|