102 lines
2.2 KiB
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"))
|
|
}
|
|
}
|
|
}
|