Download api/src/security/api-keys.js from Dieiol919/cobalt: direct link, hf CLI and curl.
- Browser
- Download file 7.85 kB
-
https://huggingface.co/Dieiol919/cobalt/resolve/main/api/src/security/api-keys.js
- Command line
-
hf download hf://Dieiol919/cobalt/api/src/security/api-keys.js
-
curl -L -o api-keys.js https://huggingface.co/Dieiol919/cobalt/resolve/main/api/src/security/api-keys.js
7.85 kB
| import { env } from "../config.js"; | |
| import { Green, Yellow } from "../misc/console-text.js"; | |
| import ip from "ipaddr.js"; | |
| import * as cluster from "../misc/cluster.js"; | |
| import { FileWatcher } from "../misc/file-watcher.js"; | |
| // this function is a modified variation of code | |
| // from https://stackoverflow.com/a/32402438/14855621 | |
| const generateWildcardRegex = rule => { | |
| var escapeRegex = (str) => str.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1"); | |
| return new RegExp("^" + rule.split("*").map(escapeRegex).join(".*") + "$"); | |
| } | |
| const UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/; | |
| let keys = {}, reader = null; | |
| const ALLOWED_KEYS = new Set(['name', 'ips', 'userAgents', 'limit', 'allowedServices']); | |
| /* Expected format pseudotype: | |
| ** type KeyFileContents = Record< | |
| ** UUIDv4String, | |
| ** { | |
| ** name?: string, | |
| ** limit?: number | "unlimited", | |
| ** ips?: CIDRString[], | |
| ** userAgents?: string[], | |
| ** allowedServices?: "all" | string[], | |
| ** } | |
| ** >; | |
| */ | |
| const validateKeys = (input) => { | |
| if (typeof input !== 'object' || input === null) { | |
| throw "input is not an object"; | |
| } | |
| if (Object.keys(input).some(x => !UUID_REGEX.test(x))) { | |
| throw "key file contains invalid key(s)"; | |
| } | |
| Object.values(input).forEach(details => { | |
| if (typeof details !== 'object' || details === null) { | |
| throw "some key(s) are incorrectly configured"; | |
| } | |
| const unexpected_key = Object.keys(details).find(k => !ALLOWED_KEYS.has(k)); | |
| if (unexpected_key) { | |
| throw "detail object contains unexpected key: " + unexpected_key; | |
| } | |
| if (details.limit && details.limit !== 'unlimited') { | |
| if (typeof details.limit !== 'number') | |
| throw "detail object contains invalid limit (not a number)"; | |
| else if (details.limit < 1) | |
| throw "detail object contains invalid limit (not a positive number)"; | |
| } | |
| if (details.ips) { | |
| if (!Array.isArray(details.ips)) | |
| throw "details object contains value for `ips` which is not an array"; | |
| const invalid_ip = details.ips.find( | |
| addr => typeof addr !== 'string' || (!ip.isValidCIDR(addr) && !ip.isValid(addr)) | |
| ); | |
| if (invalid_ip) { | |
| throw "`ips` in details contains an invalid IP or CIDR range: " + invalid_ip; | |
| } | |
| } | |
| if (details.userAgents) { | |
| if (!Array.isArray(details.userAgents)) | |
| throw "details object contains value for `userAgents` which is not an array"; | |
| const invalid_ua = details.userAgents.find(ua => typeof ua !== 'string'); | |
| if (invalid_ua) { | |
| throw "`userAgents` in details contains an invalid user agent: " + invalid_ua; | |
| } | |
| } | |
| if (details.allowedServices) { | |
| if (Array.isArray(details.allowedServices)) { | |
| const invalid_services = details.allowedServices.some( | |
| service => !env.allServices.has(service) | |
| ); | |
| if (invalid_services) { | |
| throw "`allowedServices` in details contains an invalid service"; | |
| } | |
| } else if (details.allowedServices !== "all") { | |
| throw "details object contains value for `allowedServices` which is not an array or `all`"; | |
| } | |
| } | |
| }); | |
| } | |
| const formatKeys = (keyData) => { | |
| const formatted = {}; | |
| for (let key in keyData) { | |
| const data = keyData[key]; | |
| key = key.toLowerCase(); | |
| formatted[key] = {}; | |
| if (data.limit) { | |
| if (data.limit === "unlimited") { | |
| data.limit = Infinity; | |
| } | |
| formatted[key].limit = data.limit; | |
| } | |
| if (data.ips) { | |
| formatted[key].ips = data.ips.map(addr => { | |
| if (ip.isValid(addr)) { | |
| const parsed = ip.parse(addr); | |
| const range = parsed.kind() === 'ipv6' ? 128 : 32; | |
| return [ parsed, range ]; | |
| } | |
| return ip.parseCIDR(addr); | |
| }); | |
| } | |
| if (data.userAgents) { | |
| formatted[key].userAgents = data.userAgents.map(generateWildcardRegex); | |
| } | |
| if (data.allowedServices) { | |
| if (Array.isArray(data.allowedServices)) { | |
| formatted[key].allowedServices = new Set(data.allowedServices); | |
| } else { | |
| formatted[key].allowedServices = data.allowedServices; | |
| } | |
| } | |
| } | |
| return formatted; | |
| } | |
| const updateKeys = (newKeys) => { | |
| validateKeys(newKeys); | |
| cluster.broadcast({ api_keys: newKeys }); | |
| keys = formatKeys(newKeys); | |
| } | |
| const loadRemoteKeys = async (source) => { | |
| updateKeys( | |
| await fetch(source).then(a => a.json()) | |
| ); | |
| } | |
| const loadLocalKeys = async () => { | |
| updateKeys( | |
| JSON.parse(await reader.read()) | |
| ); | |
| } | |
| const wrapLoad = (url, initial = false) => { | |
| let load = loadRemoteKeys.bind(null, url); | |
| if (url.protocol === 'file:') { | |
| if (initial) { | |
| reader = FileWatcher.fromFileProtocol(url); | |
| reader.on('file-updated', () => wrapLoad(url)); | |
| } | |
| load = loadLocalKeys; | |
| } | |
| load().then(() => { | |
| if (initial || reader) { | |
| console.log(`${Green('[✓]')} api keys loaded successfully!`) | |
| } | |
| }) | |
| .catch((e) => { | |
| console.error(`${Yellow('[!]')} Failed loading API keys at ${new Date().toISOString()}.`); | |
| console.error('Error:', e); | |
| }) | |
| } | |
| const err = (reason) => ({ success: false, error: reason }); | |
| export const validateAuthorization = (req) => { | |
| const authHeader = req.get('Authorization'); | |
| if (typeof authHeader !== 'string') { | |
| return err("missing"); | |
| } | |
| const [ authType, keyString ] = authHeader.split(' ', 2); | |
| if (authType.toLowerCase() !== 'api-key') { | |
| return err("not_api_key"); | |
| } | |
| if (!UUID_REGEX.test(keyString) || `${authType} ${keyString}` !== authHeader) { | |
| return err("invalid"); | |
| } | |
| const matchingKey = keys[keyString.toLowerCase()]; | |
| if (!matchingKey) { | |
| return err("not_found"); | |
| } | |
| if (matchingKey.ips) { | |
| let addr; | |
| try { | |
| addr = ip.parse(req.ip); | |
| } catch { | |
| return err("invalid_ip"); | |
| } | |
| const ip_allowed = matchingKey.ips.some( | |
| ([ allowed, size ]) => { | |
| return addr.kind() === allowed.kind() | |
| && addr.match(allowed, size); | |
| } | |
| ); | |
| if (!ip_allowed) { | |
| return err("ip_not_allowed"); | |
| } | |
| } | |
| if (matchingKey.userAgents) { | |
| const userAgent = req.get('User-Agent'); | |
| if (!matchingKey.userAgents.some(regex => regex.test(userAgent))) { | |
| return err("ua_not_allowed"); | |
| } | |
| } | |
| req.rateLimitKey = keyString.toLowerCase(); | |
| req.rateLimitMax = matchingKey.limit; | |
| return { success: true }; | |
| } | |
| export const setup = (url) => { | |
| if (cluster.isPrimary) { | |
| wrapLoad(url, true); | |
| if (env.keyReloadInterval > 0 && url.protocol !== 'file:') { | |
| setInterval(() => wrapLoad(url), env.keyReloadInterval * 1000); | |
| } | |
| } else if (cluster.isWorker) { | |
| process.on('message', (message) => { | |
| if ('api_keys' in message) { | |
| updateKeys(message.api_keys); | |
| } | |
| }); | |
| } | |
| } | |
| export const getAllowedServices = (key) => { | |
| if (typeof key !== "string") return; | |
| const allowedServices = keys[key.toLowerCase()]?.allowedServices; | |
| if (!allowedServices) return; | |
| if (allowedServices === "all") { | |
| return env.allServices; | |
| } | |
| return allowedServices; | |
| } | |