Cookies: Provide a mechanism for per user control over cookies (#61566)
This commit is contained in:
@@ -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"`
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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 {
|
||||
|
||||
@@ -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
@@ -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")
|
||||
|
||||
Reference in New Issue
Block a user