Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: expanded product admin permissions #5428

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion imports/collections/schemas/shops.js
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,7 @@ registerSchema("StorefrontUrls", StorefrontUrls);
* @property {String} unitsOfMeasure.$.label default value: `Ounces`
* @property {Boolean} unitsOfMeasure.$.default default value: `false`
* @property {Metafield[]} metafields optional
* @property {String[]} defaultSellerRoles default values: `["owner", "admin", "seller", "guest", "manage-users", "orders", "account/profile", "product", "createProduct", "tag", "index", "cart/completed"]`
* @property {String[]} defaultSellerRoles default values: `["owner", "admin", "seller", "guest", "manage-users", "orders", "account/profile", "product", "createProduct", "product/admin", tag", "index", "cart/completed"]`
* @property {Layout[]} layout optional
* @property {ShopTheme} theme optional
* @property {BrandAsset[]} brandAssets optional
Expand Down Expand Up @@ -496,6 +496,7 @@ export const Shop = new SimpleSchema({
"account/profile",
"product",
"createProduct",
"product/admin",
"tag",
"index",
"cart/completed"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export const defaultOwnerRoles = [
"index",
"owner",
"product",
"product/admin",
"shopSettings",
"tag"
];
Expand All @@ -39,6 +40,7 @@ export const defaultShopManagerRoles = [
"guest",
"index",
"product",
"product/admin",
"shopSettings",
"tag"
];
34 changes: 17 additions & 17 deletions imports/plugins/core/catalog/server/methods/catalog.js
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ Meteor.methods({

const authUserId = Reaction.getUserId();

if (!Reaction.hasPermission("createProduct", authUserId, variant.shopId)) {
if (!Reaction.hasPermission(["createProduct", "product/admin", "product/clone"], authUserId, variant.shopId)) {
throw new ReactionError("access-denied", "Access Denied");
}

Expand Down Expand Up @@ -384,7 +384,7 @@ Meteor.methods({
}

const userId = Reaction.getUserId();
if (!Reaction.hasPermission("createProduct", userId, product.shopId)) {
if (!Reaction.hasPermission(["createProduct", "product/admin", "product/create"], userId, product.shopId)) {
throw new ReactionError("access-denied", "Access Denied");
}

Expand Down Expand Up @@ -438,7 +438,7 @@ Meteor.methods({
}

const authUserId = Reaction.getUserId();
if (!Reaction.hasPermission("createProduct", authUserId, variant.shopId)) {
if (!Reaction.hasPermission(["createProduct", "product/admin", "product/archive"], authUserId, variant.shopId)) {
throw new ReactionError("access-denied", "Access Denied");
}

Expand Down Expand Up @@ -504,7 +504,7 @@ Meteor.methods({

// REVIEW: This check may be unnecessary now - checks that user has permission to clone
// for active shop
if (!Reaction.hasPermission("createProduct")) {
if (!Reaction.hasPermission(["createProduct", "product/admin", "product/clone"])) {
throw new ReactionError("access-denied", "Access Denied");
}

Expand All @@ -521,11 +521,11 @@ Meteor.methods({

// For each unique shopId check to make sure that user has permission to clone
uniqueShopIds.forEach((shopId) => {
if (!Reaction.hasPermission("createProduct", this.userId, shopId)) {
if (!Reaction.hasPermission(["createProduct", "product/admin", "product/clone"], this.userId, shopId)) {
throw new ReactionError("access-denied", "Access Denied");
}
});
} else if (!Reaction.hasPermission("createProduct", this.userId, productOrArray.shopId)) {
} else if (!Reaction.hasPermission(["createProduct", "product/admin", "product/clone"], this.userId, productOrArray.shopId)) {
// Single product was passed in - ensure that user has permission to clone
throw new ReactionError("access-denied", "Access Denied");
}
Expand Down Expand Up @@ -635,7 +635,7 @@ Meteor.methods({
*/
"products/createProduct"() {
// Ensure user has createProduct permission for active shop
if (!Reaction.hasPermission("createProduct")) {
if (!Reaction.hasPermission(["createProduct", "product/admin", "product/create"])) {
throw new ReactionError("access-denied", "Access Denied");
}

Expand Down Expand Up @@ -676,7 +676,7 @@ Meteor.methods({

const authUserId = Reaction.getUserId();

if (!Reaction.hasPermission("createProduct", authUserId, product.shopId)) {
if (!Reaction.hasPermission(["createProduct", "product/admin", "product/archive"], authUserId, product.shopId)) {
throw new ReactionError("access-denied", "Access Denied");
}

Expand Down Expand Up @@ -802,7 +802,7 @@ Meteor.methods({
throw new ReactionError("not-found", "Product not found");
}

if (!Reaction.hasPermission("createProduct", this.userId, doc.shopId)) {
if (!Reaction.hasPermission(["createProduct", "product/admin", "product/update"], this.userId, doc.shopId)) {
throw new ReactionError("access-denied", "Access Denied");
}

Expand Down Expand Up @@ -882,7 +882,7 @@ Meteor.methods({
throw new ReactionError("not-found", "Product not found");
}

if (!Reaction.hasPermission("createProduct", this.userId, product.shopId)) {
if (!Reaction.hasPermission(["createProduct", "product/admin", "product/update"], this.userId, product.shopId)) {
throw new ReactionError("access-denied", "Access Denied");
}

Expand Down Expand Up @@ -963,7 +963,7 @@ Meteor.methods({
const product = Products.findOne(productId);
if (!product) {
throw new ReactionError("not-found", "Product not found");
} else if (!Reaction.hasPermission("createProduct", this.userId, product.shopId)) {
} else if (!Reaction.hasPermission(["createProduct", "product/admin", "product/update"], this.userId, product.shopId)) {
throw new ReactionError("access-denied", "Access Denied");
}

Expand Down Expand Up @@ -998,7 +998,7 @@ Meteor.methods({
const product = Products.findOne(productId);
if (!product) {
throw new ReactionError("not-found", "Product not found");
} else if (!Reaction.hasPermission("createProduct", this.userId, product.shopId)) {
} else if (!Reaction.hasPermission(["createProduct", "product/admin", "product/update"], this.userId, product.shopId)) {
throw new ReactionError("access-denied", "Access Denied");
}

Expand Down Expand Up @@ -1033,7 +1033,7 @@ Meteor.methods({
const product = Products.findOne(productId);
if (!product) {
throw new ReactionError("not-found", "Product not found");
} else if (!Reaction.hasPermission("createProduct", this.userId, product.shopId)) {
} else if (!Reaction.hasPermission(["createProduct", "product/admin", "product/update"], this.userId, product.shopId)) {
throw new ReactionError("access-denied", "Access Denied");
}

Expand Down Expand Up @@ -1101,7 +1101,7 @@ Meteor.methods({
// This checks to make sure the user has createProduct permissions for the active shop.
// TODO: We should determine if that is the correct role that a user should have
// to be permitted to re-arrange products on the grid
if (!Reaction.hasPermission("createProduct", this.userId, shopId)) {
if (!Reaction.hasPermission(["createProduct", "product/admin", "product/update"], this.userId, shopId)) {
throw new ReactionError("access-denied", "Access Denied");
}

Expand Down Expand Up @@ -1145,7 +1145,7 @@ Meteor.methods({
const product = Products.findOne(productId);
if (!product) {
throw new ReactionError("not-found", "Product not found");
} else if (!Reaction.hasPermission("createProduct", this.userId, product.shopId)) {
} else if (!Reaction.hasPermission(["createProduct", "product/admin", "product/update"], this.userId, product.shopId)) {
throw new ReactionError("access-denied", "Access Denied");
}

Expand Down Expand Up @@ -1219,7 +1219,7 @@ Meteor.methods({
const product = Products.findOne(productId);
if (!product) {
throw new ReactionError("not-found", "Product not found");
} else if (!Reaction.hasPermission("createProduct", this.userId, product.shopId)) {
} else if (!Reaction.hasPermission(["createProduct", "product/admin", "product/update"], this.userId, product.shopId)) {
throw new ReactionError("access-denied", "Access Denied");
}

Expand Down Expand Up @@ -1253,7 +1253,7 @@ Meteor.methods({
throw new ReactionError("not-found", "Product not found");
}

if (!Reaction.hasPermission("createProduct", this.userId, product.shopId)) {
if (!Reaction.hasPermission(["createProduct", "product/admin", "product/update"], this.userId, product.shopId)) {
throw new ReactionError("access-denied", "Access Denied");
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export default async function publishProducts(context, productIds) {
if (!isInternalCall) {
const uniqueShopIds = uniq(products.map((product) => product.shopId));
uniqueShopIds.forEach((shopId) => {
if (!userHasPermission(["createProduct"], shopId)) {
if (!userHasPermission(["createProduct", "product/admin", "product/publish"], shopId)) {
throw new ReactionError("access-denied", "Access Denied");
}
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3338,6 +3338,7 @@ export default {
"account/profile",
"product",
"createProduct",
"product/admin",
"tag",
"index",
"cart/completed"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ Meteor.publish("ProductGridMedia", function productGridMediaPublish(productIds)
// Product editors can see both published and unpublished images
// There is an implied shopId in Reaction.hasPermission that defaults to
// the active shopId via Reaction.getShopId
if (!Reaction.hasPermission(["createProduct"], this.userId)) {
if (!Reaction.hasPermission(["createProduct", "product/admin", "product/publish"], this.userId)) {
selector["metadata.workflow"].$in = [null, "published"];
}

Expand Down Expand Up @@ -62,7 +62,7 @@ Meteor.publish("ProductMedia", function productMediaPublish(id) {
// Product editors can see both published and unpublished images
// There is an implied shopId in Reaction.hasPermission that defaults to
// the active shopId via Reaction.getShopId
if (!Reaction.hasPermission(["createProduct"], this.userId)) {
if (!Reaction.hasPermission(["createProduct", "product/admin", "product/publish"], this.userId)) {
selector["metadata.workflow"].$in = [null, "published"];
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ Meteor.publish("Product", function (productIdOrHandle, shopIdOrSlug) {

// Authorized content curators for the shop get special publication of the product
// all all relevant revisions all is one package
if (Reaction.hasPermission(["owner", "createProduct"], this.userId, product.shopId)) {
if (Reaction.hasPermission(["owner", "createProduct", "product/admin", "product/update"], this.userId, product.shopId)) {
selector.isVisible = {
$in: [true, false, undefined]
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,7 @@ Meteor.publish("Products", function (productScrollLimit = 24, productFilters, so
}

// Get a list of shopIds that this user has "createProduct" permissions for (owner permission is checked by default)
const userAdminShopIds = Reaction.getShopsWithRoles(["createProduct"], this.userId) || [];
const userAdminShopIds = Reaction.getShopsWithRoles(["createProduct", "product/admin"], this.userId) || [];

// We publish an admin version of this publication to admins of products who are in "Edit Mode"
if (editMode) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ Meteor.publish("Tags", function (tagIds) {
const shopId = Reaction.getShopId();

// Only let users what have createProduct permissions see the tags
if (!Reaction.hasPermission(["createProduct"], this.userId)) {
if (!Reaction.hasPermission(["createProduct", "product/admin", "product/update"], this.userId)) {
return this.ready();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ const {
* database operation is executed in a server method.
*/

/**
* @description security definitions for collections
* @returns {undefined} undefined
*/
export default function () {
/*
* Define some additional rule chain methods
Expand Down Expand Up @@ -126,7 +130,7 @@ export default function () {

Security.permit(["insert", "update", "remove"])
.collections([MediaRecords])
.ifHasRoleForActiveShop({ role: ["admin", "owner", "createProduct"] })
.ifHasRoleForActiveShop({ role: ["admin", "owner", "createProduct", "product/admin", "product/update"] })
.ifFileBelongsToShop();

/*
Expand All @@ -145,7 +149,7 @@ export default function () {
*/

Products.permit(["insert", "update", "remove"])
.ifHasRoleForActiveShop({ role: ["createProduct"] })
.ifHasRoleForActiveShop({ role: ["createProduct", "product/admin"] })
.ifShopIdMatches()
.allowInClientCode();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ function composer(props, onData) {
dashboardHeaderTemplate: props.data.dashboardHeader,
isActionViewAtRootView: Reaction.isActionViewAtRootView(),
actionViewIsOpen: Reaction.isActionViewOpen(),
hasCreateProductAccess: Reaction.hasPermission("createProduct", Reaction.getUserId(), Reaction.getShopId()),
hasCreateProductAccess: Reaction.hasPermission(["createProduct", "product/admin", "product/create"], Reaction.getUserId(), Reaction.getShopId()),
shopId: Reaction.getShopId(),
shops,

Expand All @@ -92,7 +92,17 @@ function composer(props, onData) {
});
}

/**
* @name ToolbarContainer
* @param {React.Component} Comp wrapped component
* @returns {React.Component} returns a React component
*/
export default function ToolbarContainer(Comp) {
/**
* @name CompositeComponent
* @param {Object} props Component props
* @returns {React.Component} Wrapped Toolbar component
*/
function CompositeComponent(props) {
return (
<AdminContextProvider>
Expand Down
8 changes: 4 additions & 4 deletions imports/plugins/core/files/server/methods.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export async function insertMedia(fileRecord) {
* @memberof Media/Methods
* @summary Unpublish a media record by updating it's workflow
* @param {String} fileRecordId - _id of file record to be deleted.
* @return {Boolean}
* @return {Boolean} was media successfully removed
*/
export async function removeMedia(fileRecordId) {
check(fileRecordId, String);
Expand Down Expand Up @@ -69,13 +69,13 @@ export async function removeMedia(fileRecordId) {
* @method
* @memberof Media/Methods
* @summary sorting media by array indexes
* @param {String[]} sortedMediaIDs
* @param {String[]} sortedMediaIDs ID's of sorted media
* @return {Boolean} true
*/
export function updateMediaPriorities(sortedMediaIDs) {
check(sortedMediaIDs, [String]);

if (!Reaction.hasPermission("createProduct")) {
if (!Reaction.hasPermission(["createProduct", "product/admin", "product/update"])) {
throw new ReactionError("access-denied", "Access Denied");
}

Expand Down Expand Up @@ -125,7 +125,7 @@ export function updateMediaPriority(mediaId, priority) {
check(mediaId, String);
check(priority, Number);

if (!Reaction.hasPermission("createProduct")) {
if (!Reaction.hasPermission(["createProduct", "product/admin", "product/update"])) {
throw new ReactionError("access-denied", "Access Denied");
}

Expand Down
2 changes: 1 addition & 1 deletion imports/plugins/core/tags/server/methods.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Meteor.methods({
check(excludedTagIds, Match.OneOf(undefined, Array));

// Return a blank result set for non admins
if (!Reaction.hasPermission(["admin", "owner", "createProduct"], this.userId)) {
if (!Reaction.hasPermission(["admin", "owner", "createProduct", "product/admin", "product/update"], this.userId)) {
return [];
}

Expand Down
2 changes: 1 addition & 1 deletion imports/plugins/core/ui/client/containers/mediaGallery.js
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ function sortMedia(media) {
*/
function composer(props, onData) {
onData(null, {
editable: Reaction.hasPermission(props.permission || ["createProduct"]),
editable: Reaction.hasPermission(props.permission || ["createProduct", "product/admin", "product/update"]),
media: sortMedia(props.media)
});
}
Expand Down
Loading