Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F3541276
D9675.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
6 KB
Referenced Files
None
Subscribers
None
D9675.diff
View Options
diff --git a/keyserver/src/scripts/validate-role-permissions.js b/keyserver/src/scripts/validate-role-permissions.js
new file mode 100644
--- /dev/null
+++ b/keyserver/src/scripts/validate-role-permissions.js
@@ -0,0 +1,162 @@
+// @flow
+
+import { getRolePermissionBlobs } from 'lib/permissions/thread-permissions.js';
+import {
+ configurableCommunityPermissions,
+ userSurfacedPermissions,
+ universalCommunityPermissions,
+} from 'lib/types/thread-permission-types.js';
+import { threadTypes } from 'lib/types/thread-types-enum.js';
+import { deepDiff, values } from 'lib/utils/objects.js';
+
+import { main } from './utils.js';
+import { SQL, dbQuery } from '../database/database.js';
+
+async function validateRolePermissions() {
+ // Get all roles for existing communities since custom roles are at a
+ // community-level rather than a thread-level.
+ const fetchRolesQuery = SQL`
+ SELECT r.id, r.name, r.permissions, r.thread, t.type, t.default_role
+ FROM roles r
+ INNER JOIN threads t
+ ON t.id = r.thread
+ WHERE t.type IN (${[
+ threadTypes.COMMUNITY_ANNOUNCEMENT_ROOT,
+ threadTypes.COMMUNITY_ROOT,
+ ]})
+ `;
+ const [results] = await dbQuery(fetchRolesQuery);
+
+ for (const result of results) {
+ const roleID = result.id.toString();
+ const roleName = result.name;
+ const existingRolePermissions = JSON.parse(result.permissions);
+ const threadID = result.thread.toString();
+ const threadType = result.type;
+ const threadDefaultRole = result.default_role.toString();
+
+ // Get the 'expected permissions' set for the role. If the role is
+ // default (Members) or Admins, these permission blobs can be retrieved
+ // by calling getRolePermissionBlobs with the threadType. Otherwise, the
+ // role is a custom role and the expected permissions are the universal
+ // community permissions assuming the role has not been edited.
+ // The case of a role being edited is handled below.
+ const expectedPermissionBlobs = getRolePermissionBlobs(threadType);
+ let baseExpectedPermissionBlob;
+ if (roleID === threadDefaultRole) {
+ baseExpectedPermissionBlob = expectedPermissionBlobs.Members;
+ } else if (roleName === 'Admins') {
+ baseExpectedPermissionBlob = expectedPermissionBlobs.Admins;
+ } else if (roleName) {
+ baseExpectedPermissionBlob = Object.fromEntries(
+ universalCommunityPermissions.map(permission => [permission, true]),
+ );
+ } else {
+ baseExpectedPermissionBlob = {};
+ }
+ console.log('====================================');
+
+ // Ideally, this should never happen, but we'll skip over this in case.
+ if (!baseExpectedPermissionBlob) {
+ console.log(
+ `Skipping role ${roleName} with ID (${roleID}) in thread ${threadID}`,
+ );
+ continue;
+ }
+
+ // Deep diff seems to compare objects one-way (so deepDiff(a, b) !==
+ // deepDiff(b, a)). This means that if a key is not in `a` but not in `b`,
+ // the diff will not include that key. As a result, we need to compare both
+ // ways to ensure that we're not missing any permission discrepancies.
+ const expectedPermissionsToExistingPermissions = deepDiff(
+ baseExpectedPermissionBlob,
+ existingRolePermissions,
+ );
+ const existingPermissionsToExpectedPermissions = deepDiff(
+ existingRolePermissions,
+ baseExpectedPermissionBlob,
+ );
+
+ console.log(
+ `Validating: Role Name (${roleName}) | Role ID (${roleID}) | ` +
+ `Thread Type (${threadType}) | Thread ID (${threadID})\n`,
+ );
+ console.log(
+ `deepDiff(baseExpectedPermissionBlob, existingRolePermissions) = ${JSON.stringify(
+ expectedPermissionsToExistingPermissions,
+ null,
+ 2,
+ )}\n`,
+ );
+ console.log(
+ `deepDiff(existingRolePermissions, baseExpectedPermissionBlob) = ${JSON.stringify(
+ existingPermissionsToExpectedPermissions,
+ null,
+ 2,
+ )}\n`,
+ );
+
+ // Now, we want to see if the permission discrepancies are due to the user
+ // editing the role. To do this, we need to identify any permission
+ // discrepancies that could be linked to a specific user-surfaced
+ // permission. This could be useful in manually parsing through the
+ // script results to 'write off' discrepancies as user role edits.
+ const userSurfacedExpectedPermissionsToExistingPermissions = new Set();
+ const userSurfacedExistingPermissionsToExpectedPermissions = new Set();
+
+ for (const permission of values(userSurfacedPermissions)) {
+ const permissionSet = Array.from(
+ configurableCommunityPermissions[permission],
+ );
+ for (const p of permissionSet) {
+ if (expectedPermissionsToExistingPermissions[p] === true) {
+ userSurfacedExpectedPermissionsToExistingPermissions.add(permission);
+ }
+ if (existingPermissionsToExpectedPermissions[p] === true) {
+ userSurfacedExistingPermissionsToExpectedPermissions.add(permission);
+ }
+ }
+ }
+
+ const expectedPermissionsToExistingPermissionsValues = values(
+ expectedPermissionsToExistingPermissions,
+ );
+ const existingPermissionsToExpectedPermissionsValues = values(
+ existingPermissionsToExpectedPermissions,
+ );
+
+ if (
+ expectedPermissionsToExistingPermissionsValues.length > 0 ||
+ existingPermissionsToExpectedPermissionsValues.length > 0
+ ) {
+ console.log(
+ `Potential permission discrepancies for role ${roleName} that ` +
+ `could be linked back to user surfaced permissions (i.e. not an ` +
+ `actual discrepancy, but rather a user edited a role): \n`,
+ );
+
+ if (expectedPermissionsToExistingPermissionsValues.length > 0) {
+ console.log(
+ `userSurfacedExpectedPermissionsToExistingPermissions = ${JSON.stringify(
+ [...userSurfacedExpectedPermissionsToExistingPermissions],
+ null,
+ 2,
+ )}\n`,
+ );
+ }
+ if (existingPermissionsToExpectedPermissionsValues.length > 0) {
+ console.log(
+ `userSurfacedExistingPermissionsToExpectedPermissions = ${JSON.stringify(
+ [...userSurfacedExistingPermissionsToExpectedPermissions],
+ null,
+ 2,
+ )}`,
+ );
+ }
+ }
+ }
+
+ console.log('====================================');
+}
+
+main([validateRolePermissions]);
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Fri, Dec 27, 6:19 AM (9 h, 29 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2711286
Default Alt Text
D9675.diff (6 KB)
Attached To
Mode
D9675: [keyserver] Write a script to compare database role permissions match expectations
Attached
Detach File
Event Timeline
Log In to Comment