diff --git a/docker/nginx/conf.d/include/location-skylink b/docker/nginx/conf.d/include/location-skylink index 7fb3d593..b0d2066e 100644 --- a/docker/nginx/conf.d/include/location-skylink +++ b/docker/nginx/conf.d/include/location-skylink @@ -68,6 +68,11 @@ access_by_lua_block { return require("skynet.account").exit_access_unauthorized() end + -- check if portal is in subscription only mode + if require("skynet.account").is_access_forbidden() then + return require("skynet.account").exit_access_forbidden() + end + -- get account limits of currently authenticated user local limits = require("skynet.account").get_account_limits() diff --git a/docker/nginx/conf.d/include/location-skynet-registry b/docker/nginx/conf.d/include/location-skynet-registry index 27a94d29..33838f70 100644 --- a/docker/nginx/conf.d/include/location-skynet-registry +++ b/docker/nginx/conf.d/include/location-skynet-registry @@ -16,6 +16,11 @@ access_by_lua_block { return require("skynet.account").exit_access_unauthorized() end + -- check if portal is in subscription only mode + if require("skynet.account").is_access_forbidden() then + return require("skynet.account").exit_access_forbidden() + end + -- get account limits of currently authenticated user local limits = require("skynet.account").get_account_limits() diff --git a/docker/nginx/conf.d/include/portal-access-check b/docker/nginx/conf.d/include/portal-access-check index 5ce1e986..1559d863 100644 --- a/docker/nginx/conf.d/include/portal-access-check +++ b/docker/nginx/conf.d/include/portal-access-check @@ -3,4 +3,9 @@ access_by_lua_block { if require("skynet.account").is_access_unauthorized() then return require("skynet.account").exit_access_unauthorized() end + + -- check if portal is in subscription only mode + if require("skynet.account").is_access_forbidden() then + return require("skynet.account").exit_access_forbidden() + end } diff --git a/docker/nginx/conf.d/server/server.api b/docker/nginx/conf.d/server/server.api index 8d68043a..bcdd3705 100644 --- a/docker/nginx/conf.d/server/server.api +++ b/docker/nginx/conf.d/server/server.api @@ -260,6 +260,11 @@ location /skynet/tus { if require("skynet.account").is_access_unauthorized() then return require("skynet.account").exit_access_unauthorized() end + + -- check if portal is in subscription only mode + if require("skynet.account").is_access_forbidden() then + return require("skynet.account").exit_access_forbidden() + end -- get account limits of currently authenticated user local limits = require("skynet.account").get_account_limits() diff --git a/docker/nginx/libs/skynet/account.lua b/docker/nginx/libs/skynet/account.lua index 1c495ae4..2e5c62e6 100644 --- a/docker/nginx/libs/skynet/account.lua +++ b/docker/nginx/libs/skynet/account.lua @@ -6,8 +6,10 @@ local anon_limits = { ["tierName"] = "anonymous", ["upload"] = 655360, ["downloa -- no limits applied local no_limits = { ["tierName"] = "internal", ["upload"] = 0, ["download"] = 0, ["maxUploadSize"] = 0, ["registry"] = 0 } --- handle request exit when access to portal should be restricted --- currently handles only HTTP_UNAUTHORIZED but can be extended in future +-- free tier name +local free_tier = "free" + +-- handle request exit when access to portal should be restricted to authenticated users only function _M.exit_access_unauthorized(message) ngx.status = ngx.HTTP_UNAUTHORIZED ngx.header["content-type"] = "text/plain" @@ -15,6 +17,14 @@ function _M.exit_access_unauthorized(message) return ngx.exit(ngx.status) end +-- handle request exit when access to portal should be restricted to subscription users only +function _M.exit_access_forbidden(message) + ngx.status = ngx.HTTP_FORBIDDEN + ngx.header["content-type"] = "text/plain" + ngx.say(message or "Portal operator restricted access to users with active subscription only") + return ngx.exit(ngx.status) +end + function _M.accounts_enabled() return os.getenv("PORTAL_MODULES"):match("a") ~= nil end @@ -57,14 +67,31 @@ function _M.is_authenticated() return limits.tierName ~= anon_limits.tierName end +-- detect whether current user has active subscription +function _M.is_subscription_account() + local limits = _M.get_account_limits() + + return limits.tierName ~= anon_limits.tierName and limits.tierName ~= free_tier +end + function _M.is_auth_required() return os.getenv("ACCOUNTS_LIMIT_ACCESS") == "authenticated" end --- check whether access to portal should be restricted +function _M.is_subscription_required() + return os.getenv("ACCOUNTS_LIMIT_ACCESS") == "subscription" +end + +-- check whether access to portal should be restricted to authenticated users only -- based on the configurable environment variable function _M.is_access_unauthorized() return _M.accounts_enabled() and _M.is_auth_required() and not _M.is_authenticated() end +-- check whether access to portal should be restricted to users with active subscription +-- based on the configurable environment variable +function _M.is_access_forbidden() + return _M.accounts_enabled() and _M.is_subscription_required() and not _M.is_subscription_account() +end + return _M diff --git a/packages/health-check/src/index.js b/packages/health-check/src/index.js index 5bf10868..444c7a9b 100644 --- a/packages/health-check/src/index.js +++ b/packages/health-check/src/index.js @@ -8,7 +8,7 @@ if (process.env.ACCOUNTS_ENABLED === "true") { if (!process.env.SKYNET_DASHBOARD_URL) { throw new Error("You need to provide SKYNET_DASHBOARD_URL environment variable when accounts are enabled"); } - if (process.env.ACCOUNTS_LIMIT_ACCESS === "authenticated") { + if (["authenticated", "subscription"].includes(process.env.ACCOUNTS_LIMIT_ACCESS)) { if (!process.env.ACCOUNTS_TEST_USER_EMAIL) { throw new Error("ACCOUNTS_TEST_USER_EMAIL cannot be empty"); } diff --git a/packages/health-check/src/utils.js b/packages/health-check/src/utils.js index 6e35f75e..1ba46e07 100644 --- a/packages/health-check/src/utils.js +++ b/packages/health-check/src/utils.js @@ -51,7 +51,7 @@ function getAuthCookie() { if (getAuthCookie.cache) return getAuthCookie.cache; // do not authenticate if it is not necessary - if (process.env.ACCOUNTS_LIMIT_ACCESS !== "authenticated") return {}; + if (!["authenticated", "subscription"].includes(process.env.ACCOUNTS_LIMIT_ACCESS)) return {}; const email = process.env.ACCOUNTS_TEST_USER_EMAIL; const password = process.env.ACCOUNTS_TEST_USER_PASSWORD;