Cookies: Provide a mechanism for per user control over cookies (#61566)

This commit is contained in:
Emil Tullstedt
2023-02-21 11:19:07 +01:00
committed by GitHub
parent 5eaaf9b9b7
commit 0caacb3333
13 changed files with 207 additions and 97 deletions
+2
View File
@@ -17,6 +17,7 @@ type UpdatePrefsCmd struct {
WeekStart string `json:"weekStart"`
QueryHistory *pref.QueryHistoryPreference `json:"queryHistory,omitempty"`
Language string `json:"language"`
Cookies []pref.CookieType `json:"cookies,omitempty"`
}
// swagger:model
@@ -32,4 +33,5 @@ type PatchPrefsCmd struct {
Language *string `json:"language,omitempty"`
QueryHistory *pref.QueryHistoryPreference `json:"queryHistory,omitempty"`
HomeDashboardUID *string `json:"homeDashboardUID,omitempty"`
Cookies []pref.CookieType `json:"cookies,omitempty"`
}
+7 -7
View File
@@ -122,13 +122,13 @@ func (hs *HTTPServer) getFrontendSettings(c *contextmodel.ReqContext) (*dtos.Fro
HelpEnabled: setting.HelpEnabled,
ProfileEnabled: setting.ProfileEnabled,
QueryHistoryEnabled: hs.Cfg.QueryHistoryEnabled,
GoogleAnalyticsId: setting.GoogleAnalyticsId,
GoogleAnalytics4Id: setting.GoogleAnalytics4Id,
GoogleAnalytics4SendManualPageViews: setting.GoogleAnalytics4SendManualPageViews,
RudderstackWriteKey: setting.RudderstackWriteKey,
RudderstackDataPlaneUrl: setting.RudderstackDataPlaneUrl,
RudderstackSdkUrl: setting.RudderstackSdkUrl,
RudderstackConfigUrl: setting.RudderstackConfigUrl,
GoogleAnalyticsId: hs.Cfg.GoogleAnalyticsID,
GoogleAnalytics4Id: hs.Cfg.GoogleAnalytics4ID,
GoogleAnalytics4SendManualPageViews: hs.Cfg.GoogleAnalytics4SendManualPageViews,
RudderstackWriteKey: hs.Cfg.RudderstackWriteKey,
RudderstackDataPlaneUrl: hs.Cfg.RudderstackDataPlaneURL,
RudderstackSdkUrl: hs.Cfg.RudderstackSDKURL,
RudderstackConfigUrl: hs.Cfg.RudderstackConfigURL,
FeedbackLinksEnabled: hs.Cfg.FeedbackLinksEnabled,
ApplicationInsightsConnectionString: hs.Cfg.ApplicationInsightsConnectionString,
ApplicationInsightsEndpointUrl: hs.Cfg.ApplicationInsightsEndpointUrl,
+11 -4
View File
@@ -48,6 +48,13 @@ func (hs *HTTPServer) setIndexViewData(c *contextmodel.ReqContext) (*dtos.IndexV
return nil, err
}
if hs.Features.IsEnabled(featuremgmt.FlagIndividualCookiePreferences) {
if !prefs.Cookies("analytics") {
settings.GoogleAnalytics4Id = ""
settings.GoogleAnalyticsId = ""
}
}
// Locale is used for some number and date/time formatting, whereas language is used just for
// translating words in the interface
acceptLangHeader := c.Req.Header.Get("Accept-Language")
@@ -110,10 +117,10 @@ func (hs *HTTPServer) setIndexViewData(c *contextmodel.ReqContext) (*dtos.IndexV
Theme: prefs.Theme,
AppUrl: appURL,
AppSubUrl: appSubURL,
GoogleAnalyticsId: setting.GoogleAnalyticsId,
GoogleAnalytics4Id: setting.GoogleAnalytics4Id,
GoogleAnalytics4SendManualPageViews: setting.GoogleAnalytics4SendManualPageViews,
GoogleTagManagerId: setting.GoogleTagManagerId,
GoogleAnalyticsId: settings.GoogleAnalyticsId,
GoogleAnalytics4Id: settings.GoogleAnalytics4Id,
GoogleAnalytics4SendManualPageViews: hs.Cfg.GoogleAnalytics4SendManualPageViews,
GoogleTagManagerId: hs.Cfg.GoogleTagManagerID,
BuildVersion: setting.BuildVersion,
BuildCommit: setting.BuildCommit,
NewGrafanaVersion: hs.grafanaUpdateChecker.LatestVersion(),
+26 -12
View File
@@ -20,6 +20,7 @@ import (
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
"github.com/grafana/grafana/pkg/services/featuremgmt"
loginservice "github.com/grafana/grafana/pkg/services/login"
pref "github.com/grafana/grafana/pkg/services/preference"
"github.com/grafana/grafana/pkg/services/secrets"
"github.com/grafana/grafana/pkg/services/user"
"github.com/grafana/grafana/pkg/setting"
@@ -398,22 +399,35 @@ func (hs *HTTPServer) trySetEncryptedCookie(ctx *contextmodel.ReqContext, cookie
return nil
}
func (hs *HTTPServer) redirectWithError(ctx *contextmodel.ReqContext, err error, v ...interface{}) {
ctx.Logger.Warn(err.Error(), v...)
if err := hs.trySetEncryptedCookie(ctx, loginErrorCookieName, getLoginExternalError(err), 60); err != nil {
hs.log.Error("Failed to set encrypted cookie", "err", err)
}
ctx.Redirect(hs.Cfg.AppSubURL + "/login")
func (hs *HTTPServer) redirectWithError(c *contextmodel.ReqContext, err error, v ...interface{}) {
c.Logger.Warn(err.Error(), v...)
c.Redirect(hs.redirectURLWithErrorCookie(c, err))
}
func (hs *HTTPServer) RedirectResponseWithError(ctx *contextmodel.ReqContext, err error, v ...interface{}) *response.RedirectResponse {
ctx.Logger.Error(err.Error(), v...)
if err := hs.trySetEncryptedCookie(ctx, loginErrorCookieName, getLoginExternalError(err), 60); err != nil {
hs.log.Error("Failed to set encrypted cookie", "err", err)
func (hs *HTTPServer) RedirectResponseWithError(c *contextmodel.ReqContext, err error, v ...interface{}) *response.RedirectResponse {
c.Logger.Error(err.Error(), v...)
location := hs.redirectURLWithErrorCookie(c, err)
return response.Redirect(location)
}
func (hs *HTTPServer) redirectURLWithErrorCookie(c *contextmodel.ReqContext, err error) string {
setCookie := true
if hs.Features.IsEnabled(featuremgmt.FlagIndividualCookiePreferences) {
prefsQuery := pref.GetPreferenceWithDefaultsQuery{UserID: c.UserID, OrgID: c.OrgID, Teams: c.Teams}
prefs, err := hs.preferenceService.GetWithDefaults(c.Req.Context(), &prefsQuery)
if err != nil {
c.Redirect(hs.Cfg.AppSubURL + "/login")
}
setCookie = prefs.Cookies("functional")
}
return response.Redirect(hs.Cfg.AppSubURL + "/login")
if setCookie {
if err := hs.trySetEncryptedCookie(c, loginErrorCookieName, getLoginExternalError(err), 60); err != nil {
hs.log.Error("Failed to set encrypted cookie", "err", err)
}
}
return hs.Cfg.AppSubURL + "/login"
}
func (hs *HTTPServer) samlEnabled() bool {
+3 -2
View File
@@ -27,15 +27,16 @@ import (
func setupSocialHTTPServerWithConfig(t *testing.T, cfg *setting.Cfg) *HTTPServer {
sqlStore := db.InitTestDB(t)
features := featuremgmt.WithFeatures()
return &HTTPServer{
Cfg: cfg,
License: &licensing.OSSLicensingService{Cfg: cfg},
SQLStore: sqlStore,
SocialService: social.ProvideService(cfg, featuremgmt.WithFeatures(), &usagestats.UsageStatsMock{}),
SocialService: social.ProvideService(cfg, features, &usagestats.UsageStatsMock{}),
HooksService: hooks.ProvideService(),
SecretsService: fakes.NewFakeSecretsService(),
Features: featuremgmt.WithFeatures(),
Features: features,
}
}
+28 -26
View File
@@ -39,7 +39,7 @@ func (hs *HTTPServer) SetHomeDashboard(c *contextmodel.ReqContext) response.Resp
} else {
queryResult, err := hs.DashboardService.GetDashboard(c.Req.Context(), &query)
if err != nil {
return response.Error(404, "Dashboard not found", err)
return response.Error(http.StatusNotFound, "Dashboard not found", err)
}
dashboardID = queryResult.ID
}
@@ -48,7 +48,7 @@ func (hs *HTTPServer) SetHomeDashboard(c *contextmodel.ReqContext) response.Resp
cmd.HomeDashboardID = dashboardID
if err := hs.preferenceService.Save(c.Req.Context(), &cmd); err != nil {
return response.Error(500, "Failed to set home dashboard", err)
return response.ErrOrFallback(http.StatusInternalServerError, "Failed to set home dashboard", err)
}
return response.Success("Home dashboard set")
@@ -71,7 +71,7 @@ func (hs *HTTPServer) getPreferencesFor(ctx context.Context, orgID, userID, team
preference, err := hs.preferenceService.Get(ctx, &prefsQuery)
if err != nil {
return response.Error(500, "Failed to get preferences", err)
return response.Error(http.StatusInternalServerError, "Failed to get preferences", err)
}
var dashboardUID string
@@ -148,7 +148,7 @@ func (hs *HTTPServer) updatePreferencesFor(ctx context.Context, orgID, userID, t
} else {
queryResult, err := hs.DashboardService.GetDashboard(ctx, &query)
if err != nil {
return response.Error(404, "Dashboard not found", err)
return response.Error(http.StatusNotFound, "Dashboard not found", err)
}
dashboardID = queryResult.ID
}
@@ -156,19 +156,20 @@ func (hs *HTTPServer) updatePreferencesFor(ctx context.Context, orgID, userID, t
dtoCmd.HomeDashboardID = dashboardID
saveCmd := pref.SavePreferenceCommand{
UserID: userID,
OrgID: orgID,
TeamID: teamId,
Theme: dtoCmd.Theme,
Language: dtoCmd.Language,
Timezone: dtoCmd.Timezone,
WeekStart: dtoCmd.WeekStart,
HomeDashboardID: dtoCmd.HomeDashboardID,
QueryHistory: dtoCmd.QueryHistory,
UserID: userID,
OrgID: orgID,
TeamID: teamId,
Theme: dtoCmd.Theme,
Language: dtoCmd.Language,
Timezone: dtoCmd.Timezone,
WeekStart: dtoCmd.WeekStart,
HomeDashboardID: dtoCmd.HomeDashboardID,
QueryHistory: dtoCmd.QueryHistory,
CookiePreferences: dtoCmd.Cookies,
}
if err := hs.preferenceService.Save(ctx, &saveCmd); err != nil {
return response.Error(500, "Failed to save preferences", err)
return response.ErrOrFallback(http.StatusInternalServerError, "Failed to save preferences", err)
}
return response.Success("Preferences updated")
@@ -193,7 +194,7 @@ func (hs *HTTPServer) PatchUserPreferences(c *contextmodel.ReqContext) response.
func (hs *HTTPServer) patchPreferencesFor(ctx context.Context, orgID, userID, teamId int64, dtoCmd *dtos.PatchPrefsCmd) response.Response {
if dtoCmd.Theme != nil && *dtoCmd.Theme != lightTheme && *dtoCmd.Theme != darkTheme && *dtoCmd.Theme != defaultTheme && *dtoCmd.Theme != systemTheme {
return response.Error(400, "Invalid theme", nil)
return response.Error(http.StatusBadRequest, "Invalid theme", nil)
}
// convert dashboard UID to ID in order to store internally if it exists in the query, otherwise take the id from query
@@ -207,7 +208,7 @@ func (hs *HTTPServer) patchPreferencesFor(ctx context.Context, orgID, userID, te
} else {
queryResult, err := hs.DashboardService.GetDashboard(ctx, &query)
if err != nil {
return response.Error(404, "Dashboard not found", err)
return response.Error(http.StatusNotFound, "Dashboard not found", err)
}
dashboardID = &queryResult.ID
}
@@ -215,19 +216,20 @@ func (hs *HTTPServer) patchPreferencesFor(ctx context.Context, orgID, userID, te
dtoCmd.HomeDashboardID = dashboardID
patchCmd := pref.PatchPreferenceCommand{
UserID: userID,
OrgID: orgID,
TeamID: teamId,
Theme: dtoCmd.Theme,
Timezone: dtoCmd.Timezone,
WeekStart: dtoCmd.WeekStart,
HomeDashboardID: dtoCmd.HomeDashboardID,
Language: dtoCmd.Language,
QueryHistory: dtoCmd.QueryHistory,
UserID: userID,
OrgID: orgID,
TeamID: teamId,
Theme: dtoCmd.Theme,
Timezone: dtoCmd.Timezone,
WeekStart: dtoCmd.WeekStart,
HomeDashboardID: dtoCmd.HomeDashboardID,
Language: dtoCmd.Language,
QueryHistory: dtoCmd.QueryHistory,
CookiePreferences: dtoCmd.Cookies,
}
if err := hs.preferenceService.Patch(ctx, &patchCmd); err != nil {
return response.Error(500, "Failed to save preferences", err)
return response.ErrOrFallback(http.StatusInternalServerError, "Failed to save preferences", err)
}
return response.Success("Preferences updated")