package rendering import ( "context" "fmt" "time" "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/infra/remotecache" "github.com/grafana/grafana/pkg/util" ) const renderKeyPrefix = "render-%s" type RenderUser struct { OrgID int64 UserID int64 OrgRole string } func (rs *RenderingService) GetRenderUser(ctx context.Context, key string) (*RenderUser, bool) { val, err := rs.RemoteCacheService.Get(ctx, fmt.Sprintf(renderKeyPrefix, key)) if err != nil { rs.log.Error("Failed to get render key from cache", "error", err) } if val != nil { if user, ok := val.(*RenderUser); ok { return user, true } } return nil, false } func setRenderKey(cache *remotecache.RemoteCache, ctx context.Context, opts AuthOpts, renderKey string, expiry time.Duration) error { err := cache.Set(ctx, fmt.Sprintf(renderKeyPrefix, renderKey), &RenderUser{ OrgID: opts.OrgID, UserID: opts.UserID, OrgRole: string(opts.OrgRole), }, expiry) return err } func generateAndSetRenderKey(cache *remotecache.RemoteCache, ctx context.Context, opts AuthOpts, expiry time.Duration) (string, error) { key, err := util.GetRandomString(32) if err != nil { return "", err } err = setRenderKey(cache, ctx, opts, key, expiry) if err != nil { return "", err } return key, nil } type longLivedRenderKeyProvider struct { cache *remotecache.RemoteCache log log.Logger renderKey string authOpts AuthOpts sessionOpts SessionOpts } func (rs *RenderingService) CreateRenderingSession(ctx context.Context, opts AuthOpts, sessionOpts SessionOpts) (Session, error) { renderKey, err := generateAndSetRenderKey(rs.RemoteCacheService, ctx, opts, sessionOpts.Expiry) if err != nil { return nil, err } return &longLivedRenderKeyProvider{ log: rs.log, renderKey: renderKey, cache: rs.RemoteCacheService, authOpts: opts, sessionOpts: sessionOpts, }, nil } func deleteRenderKey(cache *remotecache.RemoteCache, log log.Logger, ctx context.Context, renderKey string) { err := cache.Delete(ctx, fmt.Sprintf(renderKeyPrefix, renderKey)) if err != nil { log.Error("Failed to delete render key", "error", err) } } type perRequestRenderKeyProvider struct { cache *remotecache.RemoteCache log log.Logger keyExpiry time.Duration } func (r *perRequestRenderKeyProvider) get(ctx context.Context, opts AuthOpts) (string, error) { return generateAndSetRenderKey(r.cache, ctx, opts, r.keyExpiry) } func (r *perRequestRenderKeyProvider) afterRequest(ctx context.Context, opts AuthOpts, renderKey string) { deleteRenderKey(r.cache, r.log, ctx, renderKey) } func (r *longLivedRenderKeyProvider) get(ctx context.Context, opts AuthOpts) (string, error) { if r.sessionOpts.RefreshExpiryOnEachRequest { err := setRenderKey(r.cache, ctx, opts, r.renderKey, r.sessionOpts.Expiry) if err != nil { r.log.Error("Failed to refresh render key", "error", err, "renderKey", r.renderKey) } } return r.renderKey, nil } func (r *longLivedRenderKeyProvider) afterRequest(ctx context.Context, opts AuthOpts, renderKey string) { // do nothing - renderKey from longLivedRenderKeyProvider is deleted only after session expires // or someone calls session.Dispose() } func (r *longLivedRenderKeyProvider) Dispose(ctx context.Context) { deleteRenderKey(r.cache, r.log, ctx, r.renderKey) }