Skip to content

Instantly share code, notes, and snippets.

@iscott
Forked from garside/console.js
Created April 10, 2024 20:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save iscott/449516d854e7021dedc7682a6ca3674c to your computer and use it in GitHub Desktop.
Save iscott/449516d854e7021dedc7682a6ca3674c to your computer and use it in GitHub Desktop.
Shopify Audit Permissions
/*!
* 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)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment