code: function permissible(postModel, action, context, unsafeAttrs, loadedPermissions, hasUserPermission, hasAppPermission, hasApiKeyPermission) {
          let isContributor, isEdit, isAdd, isDestroy;
  
          function isChanging(attr) {
              return unsafeAttrs[attr] && unsafeAttrs[attr] !== postModel.get(attr);
          }
  
          function isPublished() {
              return unsafeAttrs.status && unsafeAttrs.status !== 'draft';
          }
  
          function isDraft() {
              return postModel.get('status') === 'draft';
          }
  
          isContributor = loadedPermissions.user && _.some(loadedPermissions.user.roles, {name: 'Contributor'});
          isEdit = (action === 'edit');
          isAdd = (action === 'add');
          isDestroy = (action === 'destroy');
  
          if (isContributor && isEdit) {
              // Only allow contributor edit if status is changing, and the post is a draft post
              hasUserPermission = !isChanging('status') && isDraft();
          } else if (isContributor && isAdd) {
              // If adding, make sure it's a draft post and has the correct ownership
              hasUserPermission = !isPublished();
          } else if (isContributor && isDestroy) {
              // If destroying, only allow contributor to destroy their own draft posts
              hasUserPermission = isDraft();
          }
  
          const excludedAttrs = [];
          if (isContributor) {
              // Note: at the moment primary_tag is a computed field,
              // meaning we don't add it to this list. However, if the primary_tag/primary_author
              // ever becomes a db field rather than a computed field, add it to this list
              // TODO: once contributors are able to edit existing tags, this can be removed
              // @TODO: we need a concept for making a diff between incoming tags and existing tags
              excludedAttrs.push('tags');
          }
  
          if (hasUserPermission && hasApiKeyPermission && hasAppPermission) {
              return Promise.resolve({excludedAttrs});
          }
  
          return Promise.reject(new common.errors.NoPermissionError({
              message: common.i18n.t('errors.models.post.notEnoughPermission')
          }));
      }
docs: NOTE: the `authors` extension is the parent of the post model. It also has a permissible function.