mirror of
https://github.com/mariadb-operator/mariadb-operator.git
synced 2025-07-31 21:08:45 +00:00
103 lines
2.7 KiB
Go
103 lines
2.7 KiB
Go
package auth
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"net/http"
|
|
"strings"
|
|
|
|
"github.com/go-logr/logr"
|
|
mdbhttp "github.com/mariadb-operator/mariadb-operator/v25/pkg/http"
|
|
authv1 "k8s.io/api/authentication/v1"
|
|
ctrlclient "sigs.k8s.io/controller-runtime/pkg/client"
|
|
)
|
|
|
|
type Trusted struct {
|
|
ServiceAccountName string
|
|
ServiceAccountNamespace string
|
|
}
|
|
|
|
func (t *Trusted) String() string {
|
|
return fmt.Sprintf("system:serviceaccount:%s:%s", t.ServiceAccountNamespace, t.ServiceAccountName)
|
|
}
|
|
|
|
type KubernetesAuth struct {
|
|
k8sClient ctrlclient.Client
|
|
trusted *Trusted
|
|
responseWriter *mdbhttp.ResponseWriter
|
|
logger logr.Logger
|
|
}
|
|
|
|
func NewKubernetesAuth(k8sClient ctrlclient.Client, trusted *Trusted, logger logr.Logger) *KubernetesAuth {
|
|
return &KubernetesAuth{
|
|
k8sClient: k8sClient,
|
|
trusted: trusted,
|
|
responseWriter: mdbhttp.NewResponseWriter(&logger),
|
|
logger: logger,
|
|
}
|
|
}
|
|
|
|
func (a *KubernetesAuth) Handler(next http.Handler) http.Handler {
|
|
fn := func(w http.ResponseWriter, r *http.Request) {
|
|
token, err := authToken(r)
|
|
if err != nil {
|
|
a.logger.V(1).Info("Error getting Authorization header", "err", err)
|
|
a.responseWriter.Write(w, http.StatusUnauthorized, newAPIError("unauthorized"))
|
|
return
|
|
}
|
|
tokenReview := &authv1.TokenReview{
|
|
Spec: authv1.TokenReviewSpec{
|
|
Token: token,
|
|
},
|
|
}
|
|
if err := a.k8sClient.Create(r.Context(), tokenReview); err != nil {
|
|
a.logger.V(1).Info("Error verifying token in TokenReview API", "err", err)
|
|
a.responseWriter.Write(w, http.StatusUnauthorized, newAPIError("unauthorized"))
|
|
return
|
|
}
|
|
if !tokenReview.Status.Authenticated {
|
|
a.logger.V(1).Info("TokenReview not valid")
|
|
a.responseWriter.Write(w, http.StatusUnauthorized, newAPIError("unauthorized"))
|
|
return
|
|
}
|
|
if tokenReview.Status.User.Username == "" {
|
|
a.logger.V(1).Info("Username not found")
|
|
a.responseWriter.Write(w, http.StatusUnauthorized, newAPIError("unauthorized"))
|
|
return
|
|
}
|
|
if a.trusted.String() != tokenReview.Status.User.Username {
|
|
a.logger.V(1).Info("Username not allowed", "username", tokenReview.Status.User.Username)
|
|
a.responseWriter.Write(w, http.StatusForbidden, newAPIError("forbidden"))
|
|
return
|
|
}
|
|
next.ServeHTTP(w, r)
|
|
}
|
|
return http.HandlerFunc(fn)
|
|
}
|
|
|
|
func authToken(r *http.Request) (string, error) {
|
|
auth := r.Header.Get("Authorization")
|
|
if auth == "" {
|
|
return "", errors.New("Authorization header not found") //nolint:staticcheck
|
|
}
|
|
parts := strings.Split(auth, "Bearer ")
|
|
if len(parts) != 2 {
|
|
return "", errors.New("invalid Authorization header")
|
|
}
|
|
return parts[1], nil
|
|
}
|
|
|
|
type apiError struct {
|
|
Message string `json:"message"`
|
|
}
|
|
|
|
func (e *apiError) Error() string {
|
|
return e.Message
|
|
}
|
|
|
|
func newAPIError(message string) error {
|
|
return &apiError{
|
|
Message: message,
|
|
}
|
|
}
|