114 lines
3.9 KiB
Go
114 lines
3.9 KiB
Go
package kek
|
|
|
|
import (
|
|
"context"
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"errors"
|
|
"net/http"
|
|
)
|
|
|
|
// CredentialAuthenticator ist die schmale Schnittstelle zu API-02s
|
|
// Service-Credential-Pruefung (internal/moduleregistry.Registry.Authenticate).
|
|
type CredentialAuthenticator interface {
|
|
Authenticate(ctx context.Context, clientID, secret string) (moduleName string, ok bool, err error)
|
|
}
|
|
|
|
// ModuleActivationChecker ist die schmale Schnittstelle zu API-02s
|
|
// Aktivierungspruefung (internal/moduleregistry.Registry.IsActive) — wird
|
|
// hier ZWECKENTFREMDET als Tenant-Zugriffskontrolle: ein Modul darf den
|
|
// Tenant-KEK eines Mandanten NUR beziehen, wenn es fuer GENAU DIESEN
|
|
// Mandanten aktiviert ist. Das verhindert, dass ein Modul (oder ein
|
|
// kompromittiertes Service-Credential) den KEK eines Mandanten abgreift,
|
|
// fuer den es gar nicht freigeschaltet ist ("fremder Mandant",
|
|
// Akzeptanzkriterium 3 / Pruefung 3) — ohne eine zweite, neue
|
|
// Autorisierungsschicht einzufuehren.
|
|
type ModuleActivationChecker interface {
|
|
IsActive(ctx context.Context, tenantSlug, moduleName string) (bool, error)
|
|
}
|
|
|
|
// TenantResolver loest einen Tenant-Slug in seine interne ID auf
|
|
// (internal/tenant.Registry.GetBySlug, TEN-01).
|
|
type TenantResolver interface {
|
|
ResolveTenantID(ctx context.Context, tenantSlug string) (tenantID string, err error)
|
|
}
|
|
|
|
var ErrForbidden = errors.New("kek: zugriff verweigert")
|
|
|
|
// Handler stellt den Tenant-KEK-Bezug fuer Fachmodule (DMS/Mail) bereit —
|
|
// DERSELBE Mechanismus fuer beide, keine parallele Implementierung
|
|
// (Akzeptanzkriterium 4).
|
|
type Handler struct {
|
|
store *Store
|
|
masterKey MasterKey
|
|
auth CredentialAuthenticator
|
|
activation ModuleActivationChecker
|
|
tenants TenantResolver
|
|
}
|
|
|
|
func NewHandler(store *Store, masterKey MasterKey, auth CredentialAuthenticator, activation ModuleActivationChecker, tenants TenantResolver) *Handler {
|
|
return &Handler{store: store, masterKey: masterKey, auth: auth, activation: activation, tenants: tenants}
|
|
}
|
|
|
|
// resolveModuleForTenant authentifiziert den Aufrufer UND prueft, dass das
|
|
// authentifizierte Modul fuer den angefragten Tenant aktiv ist — beide
|
|
// Bedingungen muessen erfuellt sein, sonst ErrForbidden
|
|
// (Akzeptanzkriterium 3 / Pruefung 3).
|
|
func (h *Handler) resolveModuleForTenant(ctx context.Context, clientID, secret, tenantSlug string) error {
|
|
moduleName, ok, err := h.auth.Authenticate(ctx, clientID, secret)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !ok {
|
|
return ErrForbidden
|
|
}
|
|
active, err := h.activation.IsActive(ctx, tenantSlug, moduleName)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !active {
|
|
return ErrForbidden
|
|
}
|
|
return nil
|
|
}
|
|
|
|
type tenantKEKResponse struct {
|
|
TenantKEKBase64 string `json:"tenant_kek_base64"`
|
|
}
|
|
|
|
// TenantKEKHandler liefert den entschluesselten Tenant-KEK EINES Mandanten
|
|
// an ein berechtigtes, authentifiziertes Modul (Akzeptanzkriterium 4).
|
|
func (h *Handler) TenantKEKHandler(w http.ResponseWriter, r *http.Request) {
|
|
clientID := r.Header.Get("X-Nexarch-Client-Id")
|
|
secret := r.Header.Get("X-Nexarch-Client-Secret")
|
|
tenantSlug := r.URL.Query().Get("tenant")
|
|
if tenantSlug == "" {
|
|
http.Error(w, "tenant-parameter fehlt", http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
if err := h.resolveModuleForTenant(r.Context(), clientID, secret, tenantSlug); err != nil {
|
|
if errors.Is(err, ErrForbidden) {
|
|
http.Error(w, "zugriff auf diesen mandanten verweigert", http.StatusForbidden)
|
|
return
|
|
}
|
|
http.Error(w, "interner fehler", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
tenantID, err := h.tenants.ResolveTenantID(r.Context(), tenantSlug)
|
|
if err != nil {
|
|
http.Error(w, "mandant nicht gefunden", http.StatusNotFound)
|
|
return
|
|
}
|
|
|
|
plainKEK, err := h.store.GetDecrypted(r.Context(), tenantID, h.masterKey)
|
|
if err != nil {
|
|
http.Error(w, "tenant-kek konnte nicht ermittelt werden", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
_ = json.NewEncoder(w).Encode(tenantKEKResponse{TenantKEKBase64: base64.StdEncoding.EncodeToString(plainKEK)})
|
|
}
|