olivetin/service/internal/auth/local_bearer.go

110 lines
2.5 KiB
Go

package auth
import (
"crypto/subtle"
"strings"
types "github.com/OliveTin/OliveTin/internal/auth/authpublic"
"github.com/OliveTin/OliveTin/internal/config"
log "github.com/sirupsen/logrus"
)
const localBearerScheme = "Bearer"
func constantTimeEqualString(a, b string) bool {
if len(a) != len(b) {
return false
}
return subtle.ConstantTimeCompare([]byte(a), []byte(b)) == 1
}
func bearerTokenFromAuthorizationHeader(authz string) (string, bool) {
idx := strings.IndexByte(authz, ' ')
if idx <= 0 {
return "", false
}
if !strings.EqualFold(authz[:idx], localBearerScheme) {
return "", false
}
token := strings.TrimSpace(authz[idx+1:])
if token == "" {
return "", false
}
return token, true
}
func localUserHasAPIKey(user *config.LocalUser) bool {
return user != nil && user.ApiKey != ""
}
func findLocalUserByAPIKey(cfg *config.Config, token string) *config.LocalUser {
for _, user := range cfg.AuthLocalUsers.Users {
if !localUserHasAPIKey(user) {
continue
}
if constantTimeEqualString(token, user.ApiKey) {
return user
}
}
return nil
}
func localBearerAuthorizationHasEmptyCredential(authz string) bool {
idx := strings.IndexByte(authz, ' ')
return idx > 0 &&
strings.EqualFold(authz[:idx], localBearerScheme) &&
strings.TrimSpace(authz[idx+1:]) == ""
}
func logLocalBearerAPIKeyParseFailure(authz string) {
if strings.TrimSpace(authz) == "" {
return
}
if localBearerAuthorizationHasEmptyCredential(authz) {
log.Debugf("Local bearer API key: rejected (empty credential after Bearer prefix)")
return
}
log.Tracef("Local bearer API key: skipped (Authorization is not a Bearer token)")
}
func checkUserFromLocalBearerApiKey(context *types.AuthCheckingContext) *types.AuthenticatedUser {
if !context.Config.AuthLocalUsers.Enabled {
log.Tracef("Local bearer API key: skipped (authLocalUsers disabled)")
return nil
}
authz := context.Request.Header.Get("Authorization")
token, ok := bearerTokenFromAuthorizationHeader(authz)
if !ok {
logLocalBearerAPIKeyParseFailure(authz)
return nil
}
log.Debugf("Local bearer API key: checking configured local user API keys")
user := findLocalUserByAPIKey(context.Config, token)
if user == nil {
log.Debugf("Local bearer API key: rejected (no matching local user)")
return nil
}
log.WithFields(log.Fields{
"username": user.Username,
"usergroup": user.Usergroup,
}).Debugf("Local bearer API key: authenticated")
return &types.AuthenticatedUser{
Username: user.Username,
UsergroupLine: user.Usergroup,
Provider: "local",
}
}