@mirrormedia/lilith-core
Version:
## Installation
182 lines (147 loc) • 6.62 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.roleCheckers = exports.owner = exports.moderator = exports.editor = exports.contributor = exports.allowRolesForUsers = exports.allowRoles = exports.allowAllRoles = exports.admin = void 0;
const accessControlStrategy = process.env.ACCESS_CONTROL_STRATEGY;
/**
* 環境變數轉 Set,並可加入預設值。
*
* 範例設定:
* ACCESS_CONTROL_STRATEGY=restricted
* ACCESS_CONTROL_RESTRICTED_QUERY_LISTS=User,Post
* ACCESS_CONTROL_RESTRICTED_UPDATE_LISTS=User,SecretList
* ACCESS_CONTROL_RESTRICTED_DELETE_LISTS=User,SecretList
*/
const parseListEnvToSet = (value, defaults = []) => {
const set = new Set(defaults);
(value || '').split(',').map(item => item.trim()).filter(Boolean).forEach(item => set.add(item));
return set;
}; // restricted 預設封鎖 User list(query/update/delete)
const defaultRestrictedQueryLists = accessControlStrategy === 'restricted' ? ['User'] : [];
const defaultRestrictedUpdateLists = accessControlStrategy === 'restricted' ? ['User'] : []; // restricted 預設封鎖所有 list 的 delete
const defaultRestrictedDeleteLists = accessControlStrategy === 'restricted' ? ['*'] : [];
const restrictedQueryLists = parseListEnvToSet(process.env.ACCESS_CONTROL_RESTRICTED_QUERY_LISTS, defaultRestrictedQueryLists);
const restrictedUpdateLists = parseListEnvToSet(process.env.ACCESS_CONTROL_RESTRICTED_UPDATE_LISTS, defaultRestrictedUpdateLists);
const restrictedDeleteLists = parseListEnvToSet(process.env.ACCESS_CONTROL_RESTRICTED_DELETE_LISTS, defaultRestrictedDeleteLists);
const bypassWithRestrictions = ({
listKey,
operation
}) => {
if (!listKey || !operation) return true;
if (operation === 'query' && restrictedQueryLists.has(listKey)) {
return false;
}
if (operation === 'update' && restrictedUpdateLists.has(listKey)) {
return false;
}
if (operation === 'delete' && (restrictedDeleteLists.has('*') || restrictedDeleteLists.has(listKey))) {
return false;
}
return true;
};
// Role configuration
const ROLES = ['admin', 'moderator', 'editor', 'contributor']; // TODO: Add owner when implemented
// const ROLES = ['admin', 'moderator', 'editor', 'contributor', 'owner'] as const
const allowRoles = (...args) => {
// 此function會返回Boolean到list.access中, true為能夠存取, false則是無存取權
switch (accessControlStrategy) {
case 'gql':
case 'preview':
{
return () => true;
}
case 'restricted':
{
// 可透過環境變數指定部分 list 的 query/update/delete 禁用
return bypassWithRestrictions;
}
case 'cms':
default:
{
return async auth => {
return await checkAccessControl(args, auth);
};
}
}
};
exports.allowRoles = allowRoles;
const allowRolesForUsers = (...args) => {
// keystone若是發現user在db中沒有任何資料,會貼心地引導我們創立一個新的user
// 然而,此CMS預設user會有access control(安全型考量)
// 若user的create access control受到限制,則adminUI將會沒有權限幫我們新增
// (陷入沒辦法登入進CMS的窘境)
// 因此在user的access control需要多判斷「如果db中沒有user存在,就暫時關閉access control用以新增user」
return async auth => {
const newArgs = [...args, isNeedToTurnOffAccessControl];
return await checkAccessControl(newArgs, auth);
};
};
exports.allowRolesForUsers = allowRolesForUsers;
const allowAllRoles = (...additionalRoles) => {
// Allow all roles defined in ROLES plus any additional roles passed as arguments
// To add new roles, add them to ROLES above
const roles = [...allStandardRoles, ...additionalRoles];
return allowRoles(...roles);
};
exports.allowAllRoles = allowAllRoles;
const isNeedToTurnOffAccessControl = async auth => {
// if no users in db, then turn off access-control for creating first user
const users = await auth.context.prisma.user.findMany();
return users.length === 0;
};
async function checkAccessControl(checkFunctionArray, auth) {
let accessControlResult = false;
for (let i = 0; i < checkFunctionArray.length; i++) {
// check是被傳入的role判斷function,admin、moderator、editor等等的
// check()將會取得決定此user能否有存取權的boolean值
const check = checkFunctionArray[i];
const checkResult = await check(auth);
if (checkResult) {
accessControlResult = checkResult;
break;
}
}
return accessControlResult;
} // Create a role checker function for a specific role
const createRoleChecker = role => {
return auth => {
var _auth$session;
// 我們可以在auth.session.data取得當下登入使用者的資料,用此來對比使用者的role
// 預設auth.session.data只有user.name
// 若要取得user.role或是其他user資料,可至auth.ts中的sessionData調整
const user = auth === null || auth === void 0 ? void 0 : (_auth$session = auth.session) === null || _auth$session === void 0 ? void 0 : _auth$session.data;
return Boolean(user && user.role === role);
};
}; // Auto-generate role checker functions from ROLES
const roleCheckersMap = ROLES.reduce((acc, role) => {
acc[role] = createRoleChecker(role);
return acc;
}, {}); // Export individual role checker functions for convenience (backward compatibility)
// Note: When adding new roles, you can use roleCheckers.newRole directly without adding here
// Or add it here if you want direct export: export const { admin, moderator, editor, contributor, newRole } = roleCheckersMap
const {
admin,
moderator,
editor,
contributor
} = roleCheckersMap; // TODO: 完成owner
// eslint-disable-next-line @typescript-eslint/no-unused-vars
exports.contributor = contributor;
exports.editor = editor;
exports.moderator = moderator;
exports.admin = admin;
const owner = async auth => {
// const user = auth?.session?.data
// if (!user) return false
// console.log(auth.content)
// // const editedList = await auth.context.prisma[auth.listKey].find()
// return Boolean(user && user.role === 'owner')
return false;
}; // Role checkers mapping - automatically generated from ROLES
// Export for programmatic access: roleCheckers.admin, roleCheckers.moderator, etc.
exports.owner = owner;
const roleCheckers = roleCheckersMap; // All roles array - automatically generated from ROLES
// All roles defined in ROLES are included
exports.roleCheckers = roleCheckers;
const allStandardRoles = ROLES.map(role => roleCheckers[role]);