Skip to main content
Glama
accessControl.ts5.74 kB
import { logger } from '@logger'; import type { NextFunction, Request, Response } from 'express'; import { HttpStatusCodes } from './httpStatusCodes'; export enum AccessRule { none = 'none', authenticated = 'authenticated', admin = 'admin', noneAuthenticated = 'not-authenticated', hasOrganization = 'has-organization', hasProject = 'has-project', hasBearer = 'has-bearer', oauth2 = 'oauth2', } export const accessControl = <R extends AccessRule | AccessRule[]>( res: Response, accessRule: R ) => { const accessRuleArray: AccessRule[] = Array.isArray(accessRule) ? accessRule : [accessRule]; const localsAuthInformation = res.locals; const { user, organization, project, authType } = localsAuthInformation; // If 'none' access rule is present, immediately return success if (accessRuleArray.includes(AccessRule.none)) { return { success: true, message: null, data: { user, organization, project, authType }, }; } let success = true; const messages: string[] = []; // Check for 'authenticated' access rule if (accessRuleArray.includes(AccessRule.authenticated)) { if (!user) { success = false; messages.push('User is not authenticated'); } } // Check for 'admin' access rule // if (accessRuleArray.includes(AccessRule.admin)) { // if (!user?.role.includes('admin')) { // success = false; // messages.push('User is not an admin'); // } // } // Check for 'not-authenticated' access rule if (accessRuleArray.includes(AccessRule.noneAuthenticated)) { if (user) { success = false; messages.push('User is authenticated'); } } // Check for 'has-organization' access rule if (accessRuleArray.includes(AccessRule.hasOrganization)) { if (!organization) { success = false; messages.push('Organization is not set'); } } // Check for 'has-project' access rule if (accessRuleArray.includes(AccessRule.hasProject)) { if (!project) { success = false; messages.push('Project is not set'); } } // Check for 'oauth2' access rule if (accessRuleArray.includes(AccessRule.oauth2)) { if (authType !== 'oauth2') { success = false; messages.push('OAuth2 authentication is required'); } } // Handle unknown access rules const knownRules = Object.values(AccessRule); const unknownRules = accessRuleArray.filter( (rule) => !knownRules.includes(rule) ); if (unknownRules.length > 0) { success = false; messages.push(`Unknown access rules: ${unknownRules.join(', ')}`); } return { success, message: messages.join(', '), data: { user, organization, project, authType }, }; }; /** * Middleware to control API access based on access rules. * * This middleware allows for multiple access rules to be passed, either as individual `AccessRule` or * an array of `AccessRule` groups. Access is granted if at least one of the following conditions is met: * * - The user satisfies all `AccessRule` within any group of rules passed as an array. * - The user satisfies any single `AccessRule` passed individually. * * Example usage: * * ```typescript * // Allow access if the user has both `hasProject` and `hasOrganization`, or if they have `admin` rights * app.use('/protected-route', apiAccessControlMiddleWare([AccessRule.hasProject, AccessRule.hasOrganization], AccessRule.admin)); * ``` * * In this example: * - The user will be granted access if they have both `hasProject` and `hasOrganization`. * - Alternatively, the user will also be granted access if they have `admin` privileges. * * @param {...(AccessRule | AccessRule[])[]} accessRules - One or more access rules or groups of access rules. * - Single `AccessRule`: The user must satisfy this rule for access to be granted. * - Array of `AccessRule`: The user must satisfy all rules in the array for access to be granted. * @returns {Function} Express middleware function that checks if the user has the required access. * * If the user does not meet any of the provided access rules, a 403 Forbidden status is returned. * * @example * // Example 1: Require admin privileges * app.use('/admin', apiAccessControlMiddleWare(AccessRule.admin)); * * @example * // Example 2: Require both project and organization access, or admin privileges * app.use('/dashboard', apiAccessControlMiddleWare([AccessRule.hasProject, AccessRule.hasOrganization], AccessRule.admin)); */ export const accessControlMiddleWare = (...accessRules: (AccessRule | AccessRule[])[]) => (_req: Request<unknown>, res: Response, next: NextFunction): void => { let hasAccess = false; // Iterate over each access rule group (either single AccessRule or an array of AccessRules) for (const ruleGroup of accessRules) { if (Array.isArray(ruleGroup)) { // If ruleGroup is an array, check if all rules in the group are satisfied const accessResults = ruleGroup.map( (rule) => accessControl(res, rule).success ); hasAccess = accessResults.every((result) => result); // All rules must be satisfied in this case } else { // Single rule: just check this one const accessResult = accessControl(res, ruleGroup); if (accessResult.success) { hasAccess = true; } } // If access is granted at any point, stop further checks if (hasAccess) { break; } } // If no access rule group was satisfied, deny access if (!hasAccess) { logger.error('Access denied'); const errorStatusCode = HttpStatusCodes.FORBIDDEN_403; res.sendStatus(errorStatusCode); return; } next(); };

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/aymericzip/intlayer'

If you have feedback or need assistance with the MCP directory API, please join our Discord server