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