1 // Copyright (C) The Arvados Authors. All rights reserved.
3 // SPDX-License-Identifier: AGPL-3.0
13 "git.arvados.org/arvados.git/sdk/go/arvados"
14 "git.arvados.org/arvados.git/sdk/go/ctxlog"
18 maxPermCacheAge = time.Hour
19 minPermCacheAge = 5 * time.Minute
22 type permChecker interface {
23 SetToken(token string)
24 Check(ctx context.Context, uuid string) (bool, error)
27 func newPermChecker(ac *arvados.Client) permChecker {
28 return &cachingPermChecker{
31 cache: make(map[string]cacheEnt),
36 type cacheEnt struct {
41 type cachingPermChecker struct {
44 cache map[string]cacheEnt
52 func (pc *cachingPermChecker) SetToken(token string) {
53 if pc.token == token {
57 pc.cache = make(map[string]cacheEnt)
60 func (pc *cachingPermChecker) Check(ctx context.Context, uuid string) (bool, error) {
62 logger := ctxlog.FromContext(ctx).
63 WithField("token", pc.token).
64 WithField("uuid", uuid)
67 if perm, ok := pc.cache[uuid]; ok && now.Sub(perm.Time) < maxPermCacheAge {
68 logger.WithField("allowed", perm.allowed).Debug("cache hit")
69 return perm.allowed, nil
72 path, err := pc.ac.PathForUUID("get", uuid)
79 ctx = arvados.ContextWithAuthorization(ctx, "Bearer "+pc.token)
80 ctx, cancel := context.WithDeadline(ctx, time.Now().Add(time.Minute))
82 var buf map[string]interface{}
83 err = pc.ac.RequestAndDecodeContext(ctx, &buf, "GET", path, nil, url.Values{
84 "include_trash": {"true"},
85 "select": {`["uuid"]`},
91 } else if txErr, ok := err.(*arvados.TransactionError); ok && pc.isNotAllowed(txErr.StatusCode) {
94 // If "context deadline exceeded", "client
95 // disconnected", HTTP 5xx, network error, etc., don't
97 logger.WithError(err).Error("lookup error")
100 logger.WithField("allowed", allowed).Debug("cache miss")
101 pc.cache[uuid] = cacheEnt{Time: now, allowed: allowed}
105 func (pc *cachingPermChecker) isNotAllowed(status int) bool {
107 case http.StatusForbidden, http.StatusUnauthorized, http.StatusNotFound:
114 func (pc *cachingPermChecker) tidy() {
115 if len(pc.cache) <= pc.maxCurrent*2 {
118 tooOld := time.Now().Add(-minPermCacheAge)
119 for uuid, t := range pc.cache {
120 if t.Before(tooOld) {
121 delete(pc.cache, uuid)
124 pc.maxCurrent = len(pc.cache)