mirror of https://github.com/grafana/grafana
Auth: Introduce pre-logout hooks + add GCOM LogoutHook (#88475)
* Introduce preLogoutHooks in authn service * Add gcom_logout_hook * Config the api token from the Grafana config file * Simplify * Add tests for logout hook * Clean up * Update * Address PR comment * Fixpull/88518/head
parent
de201c5cdd
commit
ed6b3e9e7c
@ -0,0 +1,65 @@ |
|||||||
|
package gcomsso |
||||||
|
|
||||||
|
import ( |
||||||
|
"bytes" |
||||||
|
"context" |
||||||
|
"encoding/json" |
||||||
|
"fmt" |
||||||
|
"log/slog" |
||||||
|
"net/http" |
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/models/usertoken" |
||||||
|
"github.com/grafana/grafana/pkg/services/auth/identity" |
||||||
|
"github.com/grafana/grafana/pkg/setting" |
||||||
|
) |
||||||
|
|
||||||
|
type gcomLogoutRequest struct { |
||||||
|
Token string `json:"idToken"` |
||||||
|
SessionID string `json:"sessionId"` |
||||||
|
} |
||||||
|
|
||||||
|
type GComSSOService struct { |
||||||
|
cfg *setting.Cfg |
||||||
|
logger *slog.Logger |
||||||
|
} |
||||||
|
|
||||||
|
func ProvideGComSSOService(cfg *setting.Cfg) *GComSSOService { |
||||||
|
return &GComSSOService{ |
||||||
|
cfg: cfg, |
||||||
|
logger: slog.Default().With("logger", "gcomsso-service"), |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func (s *GComSSOService) LogoutHook(ctx context.Context, user identity.Requester, sessionToken *usertoken.UserToken) error { |
||||||
|
s.logger.Debug("Logging out from Grafana.com", "user", user.GetID(), "session", sessionToken.Id) |
||||||
|
data, err := json.Marshal(&gcomLogoutRequest{ |
||||||
|
Token: user.GetIDToken(), |
||||||
|
SessionID: fmt.Sprint(sessionToken.Id), |
||||||
|
}) |
||||||
|
if err != nil { |
||||||
|
s.logger.Error("failed to marshal request", "error", err) |
||||||
|
return err |
||||||
|
} |
||||||
|
|
||||||
|
hReq, err := http.NewRequestWithContext(ctx, http.MethodPost, s.cfg.GrafanaComURL+"/api/logout/grafana/sso", bytes.NewReader(data)) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
hReq.Header.Add("Content-Type", "application/json") |
||||||
|
hReq.Header.Add("Authorization", "Bearer "+s.cfg.GrafanaComSSOAPIToken) |
||||||
|
|
||||||
|
c := http.DefaultClient |
||||||
|
resp, err := c.Do(hReq) |
||||||
|
if err != nil { |
||||||
|
s.logger.Error("failed to send request", "error", err) |
||||||
|
return err |
||||||
|
} |
||||||
|
// nolint: errcheck
|
||||||
|
defer resp.Body.Close() |
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusNoContent { |
||||||
|
return fmt.Errorf("failed to logout from grafana com: %d", resp.StatusCode) |
||||||
|
} |
||||||
|
|
||||||
|
return nil |
||||||
|
} |
@ -0,0 +1,64 @@ |
|||||||
|
package gcomsso |
||||||
|
|
||||||
|
import ( |
||||||
|
"context" |
||||||
|
"net/http" |
||||||
|
"net/http/httptest" |
||||||
|
"testing" |
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/models/usertoken" |
||||||
|
"github.com/grafana/grafana/pkg/services/user" |
||||||
|
"github.com/grafana/grafana/pkg/setting" |
||||||
|
"github.com/stretchr/testify/require" |
||||||
|
) |
||||||
|
|
||||||
|
func TestGComSSOService_LogoutHook(t *testing.T) { |
||||||
|
cfg := &setting.Cfg{ |
||||||
|
GrafanaComURL: "http://example.com", |
||||||
|
GrafanaComSSOAPIToken: "sso-api-token", |
||||||
|
} |
||||||
|
|
||||||
|
s := ProvideGComSSOService(cfg) |
||||||
|
|
||||||
|
t.Run("Successfully logs out from grafana.com", func(t *testing.T) { |
||||||
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
||||||
|
require.Equal(t, http.MethodPost, r.Method) |
||||||
|
require.Equal(t, "/api/logout/grafana/sso", r.URL.Path) |
||||||
|
|
||||||
|
require.Equal(t, "application/json", r.Header.Get("Content-Type")) |
||||||
|
require.Equal(t, "Bearer "+cfg.GrafanaComSSOAPIToken, r.Header.Get("Authorization")) |
||||||
|
|
||||||
|
w.WriteHeader(http.StatusNoContent) |
||||||
|
})) |
||||||
|
defer server.Close() |
||||||
|
|
||||||
|
cfg.GrafanaComURL = server.URL |
||||||
|
user := &user.SignedInUser{ |
||||||
|
IDToken: "id-token", |
||||||
|
} |
||||||
|
sessionToken := &usertoken.UserToken{ |
||||||
|
Id: 123, |
||||||
|
} |
||||||
|
|
||||||
|
err := s.LogoutHook(context.Background(), user, sessionToken) |
||||||
|
require.NoError(t, err) |
||||||
|
}) |
||||||
|
|
||||||
|
t.Run("Fails to log out from grafana.com", func(t *testing.T) { |
||||||
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
||||||
|
w.WriteHeader(http.StatusInternalServerError) |
||||||
|
})) |
||||||
|
defer server.Close() |
||||||
|
|
||||||
|
cfg.GrafanaComURL = server.URL |
||||||
|
user := &user.SignedInUser{ |
||||||
|
IDToken: "id-token", |
||||||
|
} |
||||||
|
sessionToken := &usertoken.UserToken{ |
||||||
|
Id: 123, |
||||||
|
} |
||||||
|
|
||||||
|
err := s.LogoutHook(context.Background(), user, sessionToken) |
||||||
|
require.Error(t, err) |
||||||
|
}) |
||||||
|
} |
Loading…
Reference in new issue