
Research
/Security News
Weaponizing Discord for Command and Control Across npm, PyPI, and RubyGems.org
Socket researchers uncover how threat actors weaponize Discord across the npm, PyPI, and RubyGems ecosystems to exfiltrate sensitive data.
@levante-framework/permissions-core
Advanced tools
A TypeScript package implementing a resource-based access control system for multi-site platforms. Designed for use in both frontend (Vue SPA) and backend (Firebase Cloud Functions) environments.
npm install permissions-service
import { PermissionService, CacheService } from 'permissions-service';
// Create cache instance (reuse across requests in Cloud Functions)
const cache = new CacheService();
// Create permission service
const permissions = new PermissionService(cache);
// Check if user can perform action on a nested resource
const canEdit = permissions.canPerformSiteAction(
user,
'site456',
'groups',
'update',
'schools' // sub-resource required for groups
);
if (canEdit) {
// User can edit schools
}
// functions/src/permissions.ts
import { PermissionService, CacheService } from 'permissions-service';
// Module-level cache for container persistence
const cache = new CacheService();
export const updateGroup = onCall(async (request) => {
const permissions = new PermissionService(cache);
const { userId, siteId } = request.auth;
// Check permission
const canUpdate = await permissions.hasPermission(
userId,
siteId,
'groups',
'update'
);
if (!canUpdate) {
throw new HttpsError('permission-denied', 'Insufficient permissions');
}
// Proceed with update
});
// composables/usePermissions.ts
import { PermissionService, CacheService } from 'permissions-service';
import { ref, computed } from 'vue';
// Session-level cache
const cache = new CacheService();
const permissions = new PermissionService(cache);
export function usePermissions() {
const currentUser = ref(null);
const currentSite = ref(null);
const canCreateGroups = computed(async () => {
if (!currentUser.value || !currentSite.value) return false;
return await permissions.hasPermission(
currentUser.value.id,
currentSite.value.id,
'groups',
'create'
);
});
return {
canCreateGroups,
hasPermission: permissions.hasPermission.bind(permissions)
};
}
The system implements a five-tier role hierarchy:
participant
- No admin dashboard accessresearch_assistant
- Read access + user creationadmin
- Subset of actions within their sitesite_admin
- Full control over their site's resourcessuper_admin
- Full system access across all sitesThe permission system uses nested sub-resources for groups
and admins
:
Group Sub-Resources:
sites
- Site-level groupsschools
- School-level groupsclasses
- Class-level groupscohorts
- Cohort-level groupsAdmin Sub-Resources:
site_admin
- Site administrator accountsadmin
- Admin accountsresearch_assistant
- Research assistant accountsFlat Resources:
assignments
- Task assignmentsusers
- User accountstasks
- System tasks{
"admin": {
"groups": {
"sites": ["read", "update"],
"schools": ["read", "update", "delete"],
"classes": ["read", "update", "delete"],
"cohorts": ["read", "update", "delete"]
},
"admins": {
"site_admin": ["read"],
"admin": ["read"],
"research_assistant": ["create", "read"]
},
"assignments": ["create", "read", "update", "delete"],
"users": ["create", "read", "update"],
"tasks": ["read"]
}
}
new PermissionService(cache?: CacheService)
canPerformSiteAction(user, siteId, resource, action, subResource?)
Check if a user has permission to perform an action on a resource within a site.
// Nested resource (requires sub-resource)
const canEditSchools = permissions.canPerformSiteAction(
user,
'site456',
'groups',
'update',
'schools' // required for nested resources
);
// Flat resource (no sub-resource needed)
const canEditUsers = permissions.canPerformSiteAction(
user,
'site456',
'users',
'update'
);
canPerformGlobalAction(user, resource, action, subResource?)
Check if a super admin can perform a global action.
const canManageAdmins = permissions.canPerformGlobalAction(
superAdminUser,
'admins',
'delete',
'admin'
);
bulkPermissionCheck(user, siteId, checks)
Bulk permission checking for multiple resource/action combinations.
const results = permissions.bulkPermissionCheck(user, 'site456', [
{ resource: 'groups', action: 'create', subResource: 'schools' },
{ resource: 'users', action: 'read' }
]);
// Returns: [{ resource: 'groups', action: 'create', subResource: 'schools', allowed: true }, ...]
getAccessibleResources(user, siteId, action)
Get flat resources the user can perform an action on.
const resources = permissions.getAccessibleResources(user, 'site456', 'create');
// Returns: ['assignments', 'users'] (only flat resources)
getAccessibleGroupSubResources(user, siteId, action)
Get group sub-resources the user can perform an action on.
const groupTypes = permissions.getAccessibleGroupSubResources(user, 'site456', 'create');
// Returns: ['schools', 'classes', 'cohorts']
getAccessibleAdminSubResources(user, siteId, action)
Get admin sub-resources the user can perform an action on.
const adminTypes = permissions.getAccessibleAdminSubResources(user, 'site456', 'create');
// Returns: ['research_assistant']
getUserRole(userId, siteId)
Get the user's role for a specific site.
const role = await permissions.getUserRole('user123', 'site456');
// Returns: 'admin' | 'site_admin' | etc.
clearUserCache(userId)
Clear cached data for a specific user.
await permissions.clearUserCache('user123');
new CacheService(defaultTtl?: number) // Default: 5 minutes
get(key)
Retrieve cached value.
const value = cache.get('user:123:permissions');
set(key, value, ttl?)
Store value in cache with optional TTL.
cache.set('user:123:permissions', permissions, 300000); // 5 minutes
delete(key)
/ clear()
Remove specific key or clear entire cache.
cache.delete('user:123:permissions');
cache.clear();
Users must have the following structure in Firestore:
interface User {
id: string;
roles: Array<{
siteId: string;
role: 'participant' | 'research_assistant' | 'admin' | 'site_admin' | 'super_admin';
}>;
userType?: 'admin' | 'student' | 'teacher' | 'caregiver';
}
The system expects a permission matrix document with nested structure:
interface PermissionMatrix {
[role: string]: {
groups: {
sites: Action[];
schools: Action[];
classes: Action[];
cohorts: Action[];
};
admins: {
site_admin: Action[];
admin: Action[];
research_assistant: Action[];
};
assignments: Action[];
users: Action[];
tasks: Action[];
};
}
interface PermissionDocument {
permissions: PermissionMatrix;
version: string;
updatedAt: string;
}
Example Document (stored at system/permissions
):
{
"permissions": {
"site_admin": {
"groups": {
"sites": ["read", "update"],
"schools": ["create", "read", "update", "delete", "exclude"],
"classes": ["create", "read", "update", "delete", "exclude"],
"cohorts": ["create", "read", "update", "delete", "exclude"]
},
"assignments": ["create", "read", "update", "delete", "exclude"],
"users": ["create", "read", "update", "delete", "exclude"],
"admins": {
"site_admin": ["create", "read"],
"admin": ["create", "read", "update", "delete", "exclude"],
"research_assistant": ["create", "read", "update", "delete"]
},
"tasks": ["create", "read", "update", "delete", "exclude"]
}
},
"version": "1.1.0",
"updatedAt": "2025-09-29T00:00:00Z"
}
The service throws specific errors for different scenarios:
try {
const canEdit = await permissions.hasPermission(userId, siteId, 'groups', 'update');
} catch (error) {
if (error.message.includes('User not found')) {
// Handle missing user
} else if (error.message.includes('Permission matrix not found')) {
// Handle missing configuration
}
}
hasPermissions()
for multiple checkshasPermissions()
for multiple permission checksnpm run build # Compile TypeScript
npm run dev # Watch mode
npm run clean # Remove dist directory
npm test # Run tests in watch mode
npm run test:run # Run tests once
npm pack # Create tarball for local testing
This package replaces organization-based permissions with resource-based permissions. Key changes:
TBD
FAQs
Shared permissions service for front-end and back-end
The npm package @levante-framework/permissions-core receives a total of 129 weekly downloads. As such, @levante-framework/permissions-core popularity was classified as not popular.
We found that @levante-framework/permissions-core demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 4 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
/Security News
Socket researchers uncover how threat actors weaponize Discord across the npm, PyPI, and RubyGems ecosystems to exfiltrate sensitive data.
Security News
Socket now integrates with Bun 1.3’s Security Scanner API to block risky packages at install time and enforce your organization’s policies in local dev and CI.
Research
The Socket Threat Research Team is tracking weekly intrusions into the npm registry that follow a repeatable adversarial playbook used by North Korean state-sponsored actors.