| name | evernote-enterprise-rbac |
| description | Implement enterprise RBAC for Evernote integrations.
Use when building multi-tenant systems, implementing
role-based access, or handling business accounts.
Trigger with phrases like "evernote enterprise", "evernote rbac",
"evernote business", "evernote permissions".
|
| allowed-tools | Read, Write, Edit, Grep |
| version | 1.0.0 |
| license | MIT |
| author | Jeremy Longshore <jeremy@intentsolutions.io> |
Evernote Enterprise RBAC
Overview
Implement role-based access control for Evernote integrations, including multi-tenant architecture, business account handling, and permission management.
Prerequisites
- Understanding of Evernote Business accounts
- Multi-tenant application architecture
- Authentication/authorization infrastructure
Evernote Permission Levels
| Level | Scope | Capabilities |
|---|
| Read | Note | View content |
| Modify | Note | Edit content |
| Full Access | Note | Edit, share, delete |
| Notebook Read | Notebook | Read all notes |
| Notebook Modify | Notebook | Edit all notes |
| Notebook Full | Notebook | Full control |
Instructions
Step 1: Permission Model
const PermissionLevel = {
NONE: 0,
READ: 1,
READ_ACTIVITY: 2,
MODIFY: 3,
FULL_ACCESS: 4
};
const NotebookPrivilegeLevel = {
READ_NOTEBOOK: 'READ_NOTEBOOK',
MODIFY_NOTEBOOK_PLUS_ACTIVITY: 'MODIFY_NOTEBOOK_PLUS_ACTIVITY',
READ_NOTEBOOK_PLUS_ACTIVITY: 'READ_NOTEBOOK_PLUS_ACTIVITY',
GROUP: 'GROUP',
FULL_ACCESS: 'FULL_ACCESS'
};
class Permission {
constructor(data) {
this.level = data.level;
this.scope = data.scope;
this.targetGuid = data.targetGuid;
this.grantedBy = data.grantedBy;
this.expiresAt = data.expiresAt;
}
canRead() {
return this.level >= PermissionLevel.;
}
() {
. >= .;
}
() {
. >= .;
}
() {
. >= .;
}
() {
. && .() > .;
}
}
. = { , , };
Step 2: Role Definitions
const Roles = {
SUPER_ADMIN: {
name: 'super_admin',
description: 'Full system access',
permissions: ['*'],
evernoteAccess: 'full'
},
ADMIN: {
name: 'admin',
description: 'Organization administrator',
permissions: [
'users:read', 'users:write',
'notebooks:read', 'notebooks:write',
'notes:read', 'notes:write',
'settings:read', 'settings:write'
],
evernoteAccess: 'full'
},
MANAGER: {
name: 'manager',
description: 'Team manager',
permissions: [
'users:read',
'notebooks:read', 'notebooks:write',
'notes:read', 'notes:write'
],
evernoteAccess: 'modify'
},
MEMBER: {
name: 'member',
description: 'Regular team member',
permissions: [
'notebooks:read',
'notes:read',
],
:
},
: {
: ,
: ,
: [
,
],
:
},
: {
: ,
: ,
: [
],
:
}
};
{
() {
. = roleConfig.;
. = roleConfig.;
. = roleConfig.;
. = roleConfig.;
}
() {
(..()) ;
..(permission);
}
() {
levels = [, , , ];
levels.(.) >= levels.(level);
}
}
. = { , };
Step 3: RBAC Service
const { Role, Roles } = require('../models/roles');
const { Permission, PermissionLevel } = require('../models/permissions');
class RBACService {
constructor(db) {
this.db = db;
this.permissionCache = new Map();
}
async assignRole(userId, roleName, organizationId = null) {
await this.db.query(`
INSERT INTO user_roles (user_id, role_name, organization_id)
VALUES ($1, $2, $3)
ON CONFLICT (user_id, organization_id) DO UPDATE SET
role_name = EXCLUDED.role_name,
updated_at = NOW()
`, [userId, roleName, organizationId]);
this.invalidateCache(userId);
}
async getUserRole(userId, organizationId = null) {
const result = await this.db.query(`
SELECT role_name FROM user_roles
WHERE user_id = $1 AND (organization_id = $2 OR organization_id IS NULL)
ORDER BY organization_id NULLS LAST
LIMIT 1
`, [userId, organizationId]);
(result.. === ) {
(.);
}
roleConfig = [result.[]..()];
roleConfig ? (roleConfig) : (.);
}
() {
role = .(userId, organizationId);
role.(permission);
}
() {
..(, [noteGuid, userId, level, grantedBy, expiresAt]);
.(userId);
}
() {
direct = ..(, [noteGuid, userId]);
(direct.. > ) {
perm = direct.[];
(!perm. || .() < (perm.).()) {
({
: perm.,
: ,
: noteGuid
});
}
}
notebook = ..(, [noteGuid, userId]);
(notebook.. > ) {
perm = notebook.[];
(!perm. || .() < (perm.).()) {
({
: perm.,
: ,
: noteGuid
});
}
}
({
: .,
: ,
: noteGuid
});
}
() {
role = .(userId, organizationId);
(role.() && role. === ) {
..(, [organizationId]);
}
..(, [organizationId, userId]);
}
() {
( key ..()) {
(key.()) {
..(key);
}
}
}
}
. = ;
Step 4: Authorization Middleware
const RBACService = require('../services/rbac-service');
function authorize(requiredPermission) {
return async (req, res, next) => {
const rbac = new RBACService(req.app.get('db'));
const userId = req.user?.id;
const organizationId = req.params.organizationId || req.user?.organizationId;
if (!userId) {
return res.status(401).json({ error: 'Authentication required' });
}
const hasPermission = await rbac.hasPermission(userId, requiredPermission, organizationId);
if (!hasPermission) {
return res.status(403).json({
error: 'Forbidden',
required: requiredPermission
});
}
next();
};
}
function authorizeNoteAccess(requiredLevel) {
return async (req, res, next) => {
rbac = (req..());
userId = req.?.;
noteGuid = req..;
(!userId) {
res.().({ : });
}
permission = rbac.(noteGuid, userId);
(!(permission, requiredLevel)) {
res.().({
: ,
: requiredLevel,
: permission.
});
}
req. = permission;
();
};
}
() {
levels = {
: permission.(),
: permission.(),
: permission.(),
: permission.()
};
levels[required] || ;
}
. = { authorize, authorizeNoteAccess };
Step 5: Multi-Tenant Support
class TenantService {
constructor(db) {
this.db = db;
}
async createOrganization(data) {
const result = await this.db.query(`
INSERT INTO organizations (name, slug, owner_id, settings)
VALUES ($1, $2, $3, $4)
RETURNING *
`, [data.name, data.slug, data.ownerId, JSON.stringify(data.settings || {})]);
const org = result.rows[0];
await this.db.query(`
INSERT INTO user_roles (user_id, role_name, organization_id)
VALUES ($1, 'admin', $2)
`, [data.ownerId, org.id]);
return org;
}
async addMember(organizationId, userId, roleName = 'member') {
const existing = await this.db.query(, [organizationId, userId]);
(existing.. > ) {
();
}
..(, [organizationId, userId]);
..(, [userId, roleName, organizationId]);
{ : };
}
() {
..(, [userId, organizationId]);
..(, [organizationId, userId]);
{ : };
}
() {
result = ..(, [organizationId]);
result.[]?. || {};
}
() {
..(, [.(settings), organizationId]);
{ : };
}
}
. = ;
Step 6: Evernote Business Integration
const Evernote = require('evernote');
class EvernoteBusinessService {
constructor(accessToken, businessToken) {
this.personalClient = new Evernote.Client({
token: accessToken,
sandbox: false
});
this.businessToken = businessToken;
}
async getBusinessUserStore() {
const userStore = this.personalClient.getUserStore();
const user = await userStore.getUser();
if (!user.businessUserInfo) {
throw new Error('User is not a business member');
}
const businessClient = new Evernote.Client({
token: this.businessToken,
sandbox: false
});
return businessClient.();
}
() {
userStore = ..();
user = userStore.();
(!user.) {
();
}
businessClient = .({
: .,
:
});
businessClient.();
}
() {
noteStore = .();
noteStore.();
}
() {
noteStore = .();
notebook = ..();
notebook. = name;
noteStore.(notebook);
}
() {
noteStore = .();
sharedNotebook = ..();
sharedNotebook. = notebookGuid;
sharedNotebook. = emails.();
sharedNotebook. = privilege;
noteStore.(sharedNotebook);
}
}
. = ;
Step 7: API Routes with RBAC
const express = require('express');
const { authorize, authorizeNoteAccess } = require('../middleware/authorization');
const router = express.Router();
router.get('/',
authorize('notes:read'),
async (req, res) => {
}
);
router.get('/:noteGuid',
authorizeNoteAccess('read'),
async (req, res) => {
}
);
router.put('/:noteGuid',
authorizeNoteAccess('modify'),
async (req, res) => {
}
);
router.delete('/:noteGuid',
authorizeNoteAccess('delete'),
async (req, res) => {
}
);
router.post('/:noteGuid/share',
authorizeNoteAccess('share'),
async (req, res) => {
}
);
. = router;
Output
- Permission and role models
- RBAC service implementation
- Authorization middleware
- Multi-tenant support
- Evernote Business integration
- Protected API routes
Database Schema
CREATE TABLE organizations (
id SERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
slug VARCHAR(100) UNIQUE NOT NULL,
owner_id INTEGER,
settings JSONB DEFAULT '{}',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE organization_members (
organization_id INTEGER REFERENCES organizations(id),
user_id INTEGER,
joined_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (organization_id, user_id)
);
CREATE TABLE user_roles (
user_id INTEGER,
role_name VARCHAR(50) NOT NULL,
organization_id INTEGER REFERENCES organizations(id),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE (user_id, organization_id)
);
CREATE TABLE note_permissions (
note_guid UUID,
user_id INTEGER,
permission_level INTEGER NOT NULL,
granted_by ,
expires_at ,
created_at ,
updated_at ,
(note_guid, user_id)
);
notebook_permissions (
notebook_guid UUID,
user_id ,
permission_level ,
granted_by ,
expires_at ,
created_at ,
updated_at ,
(notebook_guid, user_id)
);
Resources
Next Steps
For migration strategies, see evernote-migration-deep-dive.