Created
February 9, 2022 16:52
-
-
Save garside/ebec3fcda69a6a65214f2431bfd42c34 to your computer and use it in GitHub Desktop.
Shopify Audit Permissions
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*! | |
* Shopify Admin Auditing | |
* | |
* Copyright (c) 2022 Eric Garside (http://sakaralife.com) | |
* Available under the MIT license: https://opensource.org/licenses/MIT | |
*/ | |
// The selector for permssion checkboxes label sets on the individual user pages (if shopify changes this) | |
const permissionSelector = 'label.Polaris-Choice_j5gzq:not([for^="Polaris"])' | |
// The selector for permission text within the checkbox label | |
const permissionTextSelector = ".Polaris-Choice__Label_2vd36" | |
// ======= Usage Instructions ======= | |
// Step 1: go to | |
// https://your-shopify-site.myshopify.com/admin/settings/account | |
// Step 2: paste the following script and hit enter. | |
// The script will take you from the account page to the first authorized user page. | |
// In the console, hit the arrow key and hit enter. | |
// The script will: | |
// - Create an indexed list of all users except the site owner and store this in local storage | |
// - Iterate over each user in the list, visiting their individual user account page | |
// - Scrape their user page and parse their permissions storing them in local storage | |
// - Create a one time permission map of machine readable permissions to shopify display permissions | |
// - Redirect back to the /admin/settings/account page when all users have been audited. | |
// | |
// All you need to be: | |
// - Someone with developer console access enabled in your browser | |
// - An admin in shopify with access to view permissions for all admins in your account | |
// - Patient. Like really fuckin' patient. Saint level patient. | |
// | |
// Your workflow is: | |
// - load the /admin/settings/account page | |
// - Paste the script in a console the first time | |
// - hit enter | |
// - wait for the script to load the first admin | |
// - hit the up arrow | |
// - hit then enter key | |
// - 10) wait for the script to load the next admin | |
// - hit the up arrow | |
// - hit the enter key | |
// - goto (10) | |
// | |
// Eventually by the mercy of the gods, the script will return you to the /admin/settings/account page. | |
// - hit the up arrow | |
// - hit the enter key | |
// - `shopifyUsersForAudit` now contains all the data you care about | |
// - `permissionMap` has the machine to human readable encoding | |
// | |
// When this is done, you can run `toCSV()` to get a nice string output and be done with this madness. | |
// | |
// Run `toCSVData()` if you're into that sort of thing. | |
// | |
// You can also extend this to audit 2FA and grab emails if you want, I didn't care to, cause not my use case. | |
// | |
// When you're finished, run `purgeData()`. | |
// | |
// Best of luck to you and your district in the upcoming hunger games. May the odds forever be in your favor. | |
// | |
// ======= DO NOT EDIT BELOW THIS LINE ======= | |
// | |
// The key to store these users in localstorage | |
const storageKey = "shopifyUsersForAudit" | |
const permissionMapKey = "permissionMap" | |
// Retrieve the users from local storage (will be null when first used) | |
let shopifyUsersForAudit = JSON.parse(window.localStorage.getItem(storageKey)) | |
let permissionMap = JSON.parse(window.localStorage.getItem(permissionMapKey)) | |
let auditing = false | |
// Persist the users into local storage | |
const persist = (users) => window.localStorage.setItem(storageKey, JSON.stringify(users)) | |
// On the first run, fetch the users to profile from the index account | |
if (shopifyUsersForAudit == null) { | |
// This will grab all links to admin user accounts. | |
// NodeList isn't an array so it can't be sliced, hence it must be first converted to an array to make it sliceable | |
// extract the first two elements, which will always be the store owner (uneditable) and the "add new" link | |
const links = Array.from(document.body.querySelectorAll('a[href^="/admin/settings/account/"]')).slice(2) | |
// Create account tracking objects out of each link | |
shopifyUsersForAudit = links.map(link => ({ name: link.text, href: link.href, permissions: null, })) | |
persist(shopifyUsersForAudit) | |
} | |
// Process user account, auditing if its permissions are unknown, or skipping if known. | |
function process(user) { | |
if (user.permissions == null) { | |
audit(user); | |
// precaution; audit should load a webpage to audit these permissions | |
return false | |
} | |
return true | |
} | |
// Audit the user account, halting the iteration on the processing | |
function audit(user) { | |
auditing = true | |
if (window.location != user.href) { | |
window.location = user.href | |
return | |
} | |
// Get all permissions on the page | |
const permissions = document.body.querySelectorAll(permissionSelector) | |
// Map the permissions (checkboxes) into a flat object of values | |
const userPermissions = {} | |
const buildPermissionMap = (permissionMap == null) | |
if (buildPermissionMap) permissionMap = {} | |
permissions.forEach(permission => { | |
const input = permission.querySelector("input") | |
if (buildPermissionMap) { | |
const text = permission.querySelector(permissionTextSelector).textContent | |
// There's a "New" badge with a hidden "Info" badge at the end of some permission lines | |
permissionMap[input.id] = text.replace("Info New", "") | |
} | |
userPermissions[input.id] = input.checked | |
}) | |
if (buildPermissionMap) window.localStorage.setItem(permissionMapKey, JSON.stringify(permissionMap)) | |
// Store the user's permissions on their object | |
user.permissions = userPermissions | |
// Persist the user object in the array | |
persist(shopifyUsersForAudit) | |
shopifyUsersForAudit.every(process) | |
auditing = false | |
} | |
shopifyUsersForAudit.every(process) | |
// when all accounts process successfully, return to the "account homepage" | |
if (!auditing) { | |
if (window.location.pathname == "/admin/settings/account") { console.log("You're done!") } | |
else { window.location.pathname = "/admin/settings/account" } | |
} | |
function toCSVData() { | |
const csv = [] | |
const headers = ["Name", "Link"] | |
const labels = ["Labels", "-"] | |
const permissions = Object.keys(permissionMap) | |
permissions.forEach(key => { | |
headers.push(key) | |
labels.push(permissionMap[key]) | |
}) | |
csv.push(headers, labels) | |
shopifyUsersForAudit.forEach(user => { | |
const row = [user.name, user.href] | |
permissions.forEach(key => row.push(user.permissions[key] ? "YES" : "NO")) | |
csv.push(row) | |
}) | |
return csv | |
} | |
function toCSV() { | |
toCSVData().map(row => row.join(",")).join("\n") | |
} | |
function purgeData() { | |
window.localStorage.removeItem(storageKey) | |
window.localStorage.removeItem(permissionMapKey) | |
} |
Naw mate sorry. Not my circus anymore so I don’t know what changed. Sorry! Best of luck. 🙏
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hi, thanks for your efforts here. Any update to this for the new UI?