ultisuite-backend/internal/api/admin/identity_providers.go
R3D347HR4Y d3c930cac6
Some checks are pending
CI / Go tests (push) Waiting to run
CI / Integration tests (push) Waiting to run
CI / DB migrations (push) Waiting to run
feat(identity-providers): add management for identity providers in admin API
- Introduced new endpoints for managing identity providers, including retrieval of redirect URIs and testing/syncing providers.
- Enhanced organization settings to include identity provider configurations, allowing for self-enrollment and domain restrictions.
- Implemented caching for access policies and added validation for identity provider secrets.
- Added integration tests to ensure proper functionality of identity provider management and policy enforcement.
2026-06-09 09:36:38 +02:00

99 lines
2.9 KiB
Go

package admin
import (
"context"
"fmt"
"net/http"
"github.com/go-chi/chi/v5"
"github.com/ultisuite/ulti-backend/internal/api/apiresponse"
"github.com/ultisuite/ulti-backend/internal/api/apivalidate"
"github.com/ultisuite/ulti-backend/internal/api/middleware"
"github.com/ultisuite/ulti-backend/internal/authentik"
)
func (s *Service) SyncIdentityProviders(ctx context.Context, actorSub, providerID string) (map[string]any, error) {
stored, _, _, err := s.loadOrgPolicyRaw(ctx)
if err != nil {
return nil, err
}
idpSection, _ := stored["identity_providers"].(map[string]any)
if idpSection == nil {
return nil, fmt.Errorf("identity_providers not configured")
}
providers, _ := idpSection["providers"].([]any)
found := false
for _, item := range providers {
pm, ok := item.(map[string]any)
if !ok {
continue
}
if id, _ := pm["id"].(string); id == providerID {
found = true
break
}
}
if !found {
return nil, fmt.Errorf("provider not found")
}
syncer := authentik.NewSourceSyncer(s.db, s.cfg)
if err := syncer.SyncSection(ctx, idpSection); err != nil {
return nil, err
}
s.logAudit(ctx, actorSub, "sync_identity_provider", map[string]any{
"provider_id": providerID,
})
return s.GetOrgSettings(ctx)
}
func (h *Handler) GetIdentityProviderRedirectURI(w http.ResponseWriter, r *http.Request) {
slug := chi.URLParam(r, "slug")
if slug == "" {
apivalidate.WriteValidationError(w, r, apivalidate.NewValidationError(
apivalidate.FieldDetail{Field: "slug", Message: "required"},
))
return
}
syncer := authentik.NewSourceSyncer(h.svc.db, h.svc.cfg)
apiresponse.WriteJSON(w, http.StatusOK, map[string]any{
"slug": slug,
"redirect_uri": syncer.RedirectURI(slug),
})
}
func (h *Handler) TestIdentityProvider(w http.ResponseWriter, r *http.Request) {
providerID := chi.URLParam(r, "providerID")
if providerID == "" {
apivalidate.WriteValidationError(w, r, apivalidate.NewValidationError(
apivalidate.FieldDetail{Field: "providerID", Message: "required"},
))
return
}
syncer := authentik.NewSourceSyncer(h.svc.db, h.svc.cfg)
if err := syncer.TestProvider(r.Context(), providerID); err != nil {
apiresponse.WriteError(w, r, http.StatusBadRequest, apiresponse.CodeInvalidRequest, err.Error(), nil)
return
}
apiresponse.WriteJSON(w, http.StatusOK, map[string]any{"ok": true})
}
func (h *Handler) SyncIdentityProvider(w http.ResponseWriter, r *http.Request) {
providerID := chi.URLParam(r, "providerID")
if providerID == "" {
apivalidate.WriteValidationError(w, r, apivalidate.NewValidationError(
apivalidate.FieldDetail{Field: "providerID", Message: "required"},
))
return
}
claims := middleware.ClaimsFromContext(r.Context())
payload, err := h.svc.SyncIdentityProviders(r.Context(), claims.Sub, providerID)
if err != nil {
h.logger.Error("sync identity provider", "error", err, "provider_id", providerID)
apivalidate.WriteInternal(w, r)
return
}
apiresponse.WriteJSON(w, http.StatusOK, payload)
}