110 lines
2.5 KiB
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",
|
|
}
|
|
}
|