{"id":15288996,"url":"https://github.com/kapouer/upcache","last_synced_at":"2025-04-13T06:52:14.024Z","repository":{"id":57387083,"uuid":"55778023","full_name":"kapouer/upcache","owner":"kapouer","description":"Caching proxy dynamically configured by upstream applications","archived":false,"fork":false,"pushed_at":"2024-04-13T20:27:32.000Z","size":342,"stargazers_count":16,"open_issues_count":14,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-04T13:05:28.842Z","etag":null,"topics":["cache","lua","memcached","nginx","proxy"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kapouer.png","metadata":{"files":{"readme":"README-lock.md","changelog":"CHANGES.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-04-08T12:40:18.000Z","updated_at":"2025-03-30T01:03:50.000Z","dependencies_parsed_at":"2024-06-21T19:05:56.551Z","dependency_job_id":"c00e086b-737d-4d37-86c6-75ab417aad50","html_url":"https://github.com/kapouer/upcache","commit_stats":{"total_commits":405,"total_committers":1,"mean_commits":405.0,"dds":0.0,"last_synced_commit":"3bb8f48bb2b589a3ff7f7f7ac728097ac64959f5"},"previous_names":[],"tags_count":57,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kapouer%2Fupcache","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kapouer%2Fupcache/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kapouer%2Fupcache/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kapouer%2Fupcache/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kapouer","download_url":"https://codeload.github.com/kapouer/upcache/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248675455,"owners_count":21143767,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cache","lua","memcached","nginx","proxy"],"created_at":"2024-09-30T15:55:31.888Z","updated_at":"2025-04-13T06:52:13.990Z","avatar_url":"https://github.com/kapouer.png","language":"JavaScript","readme":"Upcache Lock\n============\n\nSimple access restrictions for the application and the proxy.\n\nIntroduction\n------------\n\nAccess restrictions often comes with a heavy price to pay regarding the ability\nto cache resources.\n\nUpcache Locks let the application dynamically setup the caching proxy (nginx with\nsrcache here) so resources cache keys can vary on user grants\nbased on how resources locks are set.\n\nHow it works\n------------\n\nClient authenticates using a Json Web Token (jwt) signed with a RSA asymmetric key.\n\nThe payload of the jwt must have a `grants` array.\n\nThe application writes HTTP response headers so the proxy gets:\n\n- the RSA public key (only if the proxy requested it)\n- the list of locks the resource varies upon.\n\nWhen a client requests a resource to the proxy:\n\n- the proxy checks if the client has a valid jwt bearer cookie\n- and checks the list of known locks that resource varies upon\n- all the client jwt grants that are listed in the list of locks are used\nto build a resource cache key\n- if the resource is not already cached or if there were no known locks,\nthe request is handed over to the application.\n\nNote that it's up to the application to make the access control checks,\nand return in the HTTP response headers (using upcache node module)\nthe complete list of potential locks for a given resource:\n**that list must not vary on user grants**\n\nUsage\n-----\n\n```js\nconst locker = require('upcache').lock({\n publicKey: \u003crsa public key\u003e,\n privateKey: \u003crsa private key\u003e,\n algorithm: 'RS256', // default value, optional\n maxAge: age in seconds, must be an integer,\n userProperty: \"user\", // default value, optional, sets req[userProperty]\n varname: \"cookie_bearer\" // default value, optional, tells where jwt is\n});\n\napp.use(locker.init);\n\napp.post(\"/login\", async (req, res, next) =\u003e {\n const user = await dblogin(req.body.login, req.body.password);\n user.grants = ['subscriber', 'editor'];\n locker.login(res, user);\n});\n\napp.get(\"/logout\", (req, res, next) =\u003e {\n locker.logout(res);\n res.sendStatus(204);\n});\n\napp.get('/api/user', locker.vary(\"id-:id\", \"webmaster\"), (req, res, next) =\u003e {\n  const user = await User.get(req.user.id);\n  if (!req.user.grants.includes('webmaster')) delete user.privateData;\n  return user;\n});\n\n```\n\nGrants\n------\n\nA jwt must carry `grants`: an array of alphanumeric strings.\n\nAccess is considered granted (or unlocked) if one grant unlocks one of the locks.\n\nLocks\n-----\n\nThe application returns lists of locks to the proxy.\n\nA lock can be any alphanumeric constant naming a grant - a *litteral* lock.\n\nOtherwise a lock is a *template* lock:\n\n- it can contain a wildcard `str*`, in which case all user grants matching\nthat lock will be used to build a cache key;\n- it can even be `*` in which case all user grants make the cache key vary;\n- it can contain a named parameter `str:key` in which case the `:key` is\nreplaced by a value in the jwt payload[key].\n\nMiddlewares and methods\n-----------------------\n\nuser is an object expected to have a `grants` array of strings.\n\nFor defining locks:\n\n- locker.init(req, res, next)\n  middleware setting up handshake and cookie name headers, and req[userProperty]\n- locker.vary(locks)\n  returns a middleware that calls locker.headers\n- locker.headers(res, locks)\n  sets response headers\n\nHelpers for jwt and cookie handling:\n\n- locker.sign(user, opts)\n  sign user with opts.hostname as issuer, opts.maxAge, returns a jwt\n- locker.login(res, user, opts)\n  calls sign and sets bearer\n- locker.logout(res)\n  unsets cookie\n\nThis library propose a general implementation for access restrictions:\n\n- locker.restrict(lockA, lockB, ...)\n  returns a middleware that sends 401/403 or let through.\n  Mind that `restrict('*')` will vary on all grants while not locking the resource;\n  also `restrict('xxx-:id')` will only lock jwt that do not have an `id` property.\n  To actually restrict by id, see examples in test/lock.js.\n  It calls locker.headers() with the list of locks.\n\nhttp response headers\n---------------------\n\n- X-Upcache-Lock\n  list of locks for the current url\n\n- X-Upcache-Lock-Var (optional, defaults to cookie_bearer)\n  The name of the ngx var that contains the json web token,\n  can be `cookie_xxx` or `http_xxx` where xxx is lowercased, and dashes\n  converted to underscores.\n\n- X-Upcache-Lock-Key (upon request)\n  when the proxy sets X-Upcache-Lock-Key=1 in a request header,\n  the application must return the rsa public key in this response header.\n","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkapouer%2Fupcache","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkapouer%2Fupcache","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkapouer%2Fupcache/lists"}