- 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.
99 lines
2.9 KiB
Go
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)
|
|
}
|