sharded-gotify/auth/authentication.go

102 lines
2.2 KiB
Go

package auth
import (
"errors"
"github.com/gin-gonic/gin"
"github.com/jmattheis/memo/model"
"strings"
)
const (
headerName = "Authorization"
headerSchema = "ApiKey "
typeAdmin = 0
typeAll = 1
typeWriteOnly = 2
)
type Database interface {
GetTokenById(id string) *model.Token
GetUserByName(name string) *model.User
GetUserById(id uint) *model.User
}
type Auth struct {
DB Database
}
func (a *Auth) RequireAdmin() gin.HandlerFunc {
return a.requireToken(typeAdmin)
}
func (a *Auth) RequireAll() gin.HandlerFunc {
return a.requireToken(typeAll)
}
func (a *Auth) RequireWrite() gin.HandlerFunc {
return a.requireToken(typeWriteOnly)
}
func (a *Auth) tokenFromQueryOrHeader(ctx *gin.Context) *model.Token {
if token := a.tokenFromQuery(ctx); token != nil {
return token
} else if token := a.tokenFromHeader(ctx); token != nil {
return token
}
return nil
}
func (a *Auth) tokenFromQuery(ctx *gin.Context) *model.Token {
if token := ctx.Request.URL.Query().Get("token"); token != "" {
return a.DB.GetTokenById(token)
}
return nil
}
func (a *Auth) tokenFromHeader(ctx *gin.Context) *model.Token {
if header := ctx.Request.Header.Get(headerName); header != "" && strings.HasPrefix(header, headerSchema) {
return a.DB.GetTokenById(strings.TrimPrefix(header, headerSchema))
}
return nil
}
func (a *Auth) userFromBasicAuth(ctx *gin.Context) *model.User {
if name, pass, ok := ctx.Request.BasicAuth(); ok {
if user := a.DB.GetUserByName(name); user != nil && ComparePassword(user.Pass, []byte(pass)) {
return user
}
}
return nil
}
func (a *Auth) isAuthenticated(checkType int, token *model.Token, user *model.User) bool {
if token == nil && user == nil {
return false
}
switch checkType {
case typeWriteOnly:
return true
case typeAll:
return user != nil || (token != nil && !token.WriteOnly)
default:
if user == nil {
user = a.DB.GetUserById(token.UserID)
}
return user != nil && user.Admin
}
}
func (a *Auth) requireToken(checkType int) gin.HandlerFunc {
return func(ctx *gin.Context) {
token := a.tokenFromQueryOrHeader(ctx)
user := a.userFromBasicAuth(ctx)
if a.isAuthenticated(checkType, token, user) {
ctx.Next()
} else {
ctx.AbortWithError(401, errors.New("could not authenticate"))
}
}
}