diff --git a/app/controllers/locomotive/api/base_controller.rb b/app/controllers/locomotive/api/base_controller.rb index 1afc9d41..8d0519b8 100644 --- a/app/controllers/locomotive/api/base_controller.rb +++ b/app/controllers/locomotive/api/base_controller.rb @@ -7,15 +7,13 @@ module Locomotive skip_before_filter :verify_authenticity_token - skip_load_and_authorize_resource - before_filter :require_account before_filter :require_site before_filter :set_locale - # before_filter :validate_site_membership + before_filter :set_current_thread_variables self.responder = Locomotive::ActionController::Responder # custom responder @@ -23,6 +21,11 @@ module Locomotive protected + def set_current_thread_variables + Thread.current[:account] = current_locomotive_account + Thread.current[:site] = current_site + end + def current_ability @current_ability ||= Ability.new(current_locomotive_account, current_site) end @@ -40,4 +43,4 @@ module Locomotive end end -end \ No newline at end of file +end diff --git a/app/controllers/locomotive/api/content_assets_controller.rb b/app/controllers/locomotive/api/content_assets_controller.rb index abde5f8c..2acd8c12 100644 --- a/app/controllers/locomotive/api/content_assets_controller.rb +++ b/app/controllers/locomotive/api/content_assets_controller.rb @@ -2,11 +2,18 @@ module Locomotive module Api class ContentAssetsController < BaseController + load_and_authorize_resource :class => Locomotive::ContentAsset + def index @content_assets = current_site.content_assets respond_with(@content_assets) end + def show + @content_asset = current_site.content_assets.find(params[:id]) + respond_with(@content_asset) + end + def create @content_asset = current_site.content_assets.create(params[:content_asset]) respond_with @content_asset, :location => main_app.locomotive_api_content_assets_url @@ -18,6 +25,12 @@ module Locomotive respond_with @content_asset, :location => main_app.locomotive_api_content_assets_url end + def destroy + @content_asset = current_site.content_assets.find(params[:id]) + @content_asset.destroy + respond_with @content_asset + end + end end end diff --git a/app/controllers/locomotive/api/content_types_controller.rb b/app/controllers/locomotive/api/content_types_controller.rb index afd4ed6a..9b5014d6 100644 --- a/app/controllers/locomotive/api/content_types_controller.rb +++ b/app/controllers/locomotive/api/content_types_controller.rb @@ -2,11 +2,18 @@ module Locomotive module Api class ContentTypesController < BaseController + load_and_authorize_resource :class => Locomotive::ContentType + def index @content_types = current_site.content_types respond_with(@content_types) end + def show + @content_type = current_site.content_types.find(params[:id]) + respond_with @content_type + end + def create @content_type = current_site.content_types.create(params[:content_type]) respond_with @content_type, :location => main_app.locomotive_api_content_types_url @@ -18,6 +25,12 @@ module Locomotive respond_with @content_type, :location => main_app.locomotive_api_content_types_url end + def destroy + @content_type = current_site.content_types.find(params[:id]) + @content_type.destroy + respond_with @content_type + end + end end end diff --git a/app/controllers/locomotive/api/current_site_controller.rb b/app/controllers/locomotive/api/current_site_controller.rb index 71665ea8..408cda14 100644 --- a/app/controllers/locomotive/api/current_site_controller.rb +++ b/app/controllers/locomotive/api/current_site_controller.rb @@ -3,7 +3,9 @@ module Locomotive class CurrentSiteController < BaseController def show - respond_with(current_site) + @site = current_site + authorize! :show, @site + respond_with(@site) end end diff --git a/app/controllers/locomotive/api/memberships_controller.rb b/app/controllers/locomotive/api/memberships_controller.rb new file mode 100644 index 00000000..c889f532 --- /dev/null +++ b/app/controllers/locomotive/api/memberships_controller.rb @@ -0,0 +1,49 @@ +module Locomotive + module Api + class MembershipsController < BaseController + + # It's an embedded document, so we'll just load manually + before_filter :load_membership, :only => [ :show, :update, :destroy ] + before_filter :load_memberships, :only => [ :index ] + + authorize_resource :class => Locomotive::Membership + + def index + respond_with(@memberships) + end + + def show + respond_with(@membership) + end + + def create + build_params = params[:membership].merge({ :role => 'author' }) # force author by default + @membership = current_site.memberships.create(build_params) + respond_with(@membership) + end + + def update + @membership.update_attributes(params[:membership]) + respond_with(@membership) + end + + def destroy + @membership.destroy + respond_with(@membership) + end + + protected + + def load_membership + @membership ||= load_memberships.find(params[:id]) + end + + def load_memberships + @memberships ||= current_site.memberships + end + + end + + end +end + diff --git a/app/controllers/locomotive/api/pages_controller.rb b/app/controllers/locomotive/api/pages_controller.rb index 2a505523..551355a4 100644 --- a/app/controllers/locomotive/api/pages_controller.rb +++ b/app/controllers/locomotive/api/pages_controller.rb @@ -2,11 +2,18 @@ module Locomotive module Api class PagesController < BaseController + load_and_authorize_resource :class => Locomotive::Page + def index @pages = current_site.pages.order_by([[:depth, :asc], [:position, :asc]]) respond_with(@pages) end + def show + @page = current_site.pages.find(params[:id]) + respond_with(@page) + end + def create @page = current_site.pages.create(params[:page]) respond_with @page, :location => main_app.locomotive_api_pages_url @@ -18,6 +25,12 @@ module Locomotive respond_with @page, :location => main_app.locomotive_api_pages_url end + def destroy + @page = current_site.pages.find(params[:id]) + @page.destroy + respond_with @page + end + end end diff --git a/app/controllers/locomotive/api/sites_controller.rb b/app/controllers/locomotive/api/sites_controller.rb new file mode 100644 index 00000000..11469abe --- /dev/null +++ b/app/controllers/locomotive/api/sites_controller.rb @@ -0,0 +1,44 @@ +module Locomotive + module Api + class SitesController < BaseController + + load_and_authorize_resource :class => Locomotive::Site + + # FIXME: the auto-loaded site won't pass authorization for show, update, or destroy + skip_load_and_authorize_resource :only => [ :show, :update, :destroy ] + + def index + @sites = Locomotive::Site.all + respond_with(@sites) + end + + def show + @site = Locomotive::Site.find(params[:id]) + authorize! :show, @site + respond_with(@site) + end + + def create + @site = Locomotive::Site.create(params[:site]) + respond_with(@site) + end + + def update + @site = Locomotive::Site.find(params[:id]) + authorize! :update, @site + @site.update_attributes(params[:site]) + respond_with @site + end + + def destroy + @site = Locomotive::Site.find(params[:id]) + authorize! :destroy, @site + @site.destroy + respond_with @site + end + + end + + end +end + diff --git a/app/controllers/locomotive/api/snippets_controller.rb b/app/controllers/locomotive/api/snippets_controller.rb index b7479745..24067166 100644 --- a/app/controllers/locomotive/api/snippets_controller.rb +++ b/app/controllers/locomotive/api/snippets_controller.rb @@ -2,11 +2,18 @@ module Locomotive module Api class SnippetsController < BaseController + load_and_authorize_resource :class => Locomotive::Snippet + def index @snippets = current_site.snippets.all respond_with(@snippets) end + def show + @snippet = current_site.snippets.find(params[:id]) + respond_with @snippet + end + def create @snippet = current_site.snippets.create(params[:snippet]) respond_with @snippet, :location => main_app.locomotive_api_snippets_url @@ -18,6 +25,12 @@ module Locomotive respond_with @snippet, :location => main_app.locomotive_api_snippets_url end + def destroy + @snippet = current_site.snippets.find(params[:id]) + @snippet.destroy + respond_with @snippet + end + end end end diff --git a/app/controllers/locomotive/api/theme_assets_controller.rb b/app/controllers/locomotive/api/theme_assets_controller.rb index c1cfae8d..c7cd0cd6 100644 --- a/app/controllers/locomotive/api/theme_assets_controller.rb +++ b/app/controllers/locomotive/api/theme_assets_controller.rb @@ -2,11 +2,18 @@ module Locomotive module Api class ThemeAssetsController < BaseController + load_and_authorize_resource :class => Locomotive::ThemeAsset + def index @theme_assets = current_site.theme_assets.all respond_with(@theme_assets) end + def show + @theme_asset = current_site.theme_assets.find(params[:id]) + respond_with @theme_asset + end + def create @theme_asset = current_site.theme_assets.create(params[:theme_asset]) respond_with @theme_asset, :location => main_app.locomotive_api_theme_assets_url @@ -18,6 +25,12 @@ module Locomotive respond_with @theme_asset, :location => main_app.locomotive_api_theme_assets_url end + def destroy + @theme_asset = current_site.theme_assets.find(params[:id]) + @theme_asset.destroy + respond_with @theme_asset + end + end end end diff --git a/app/models/locomotive/ability.rb b/app/models/locomotive/ability.rb index db42da23..f5f841f4 100644 --- a/app/models/locomotive/ability.rb +++ b/app/models/locomotive/ability.rb @@ -37,6 +37,8 @@ module Locomotive can :touch, Site do |site| site == @site end + + can :read, ContentType end def setup_designer_permissions! diff --git a/config/routes.rb b/config/routes.rb index f69a9e31..b6974a20 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -68,6 +68,10 @@ Rails.application.routes.draw do resources :content_entries, :path => 'content_types/:slug/entries' + resources :sites + + resources :memberships + resource :current_site, :controller => 'current_site' end diff --git a/features/api/authorization/content_assets.feature b/features/api/authorization/content_assets.feature new file mode 100644 index 00000000..ff3f8aec --- /dev/null +++ b/features/api/authorization/content_assets.feature @@ -0,0 +1,147 @@ +Feature: Content Assets + In order to ensure content assets are not tampered with + As an admin, designer or author + I will be restricted based on my role + + Background: + Given I have the site: "test site" set up + And I have the following content assets: + | id | file | + | 4f832c2cb0d86d3f42fffffe | 5k.png | + | 4f832c2cb0d86d3f42ffffff | 5k_2.png | + And I have a designer and an author + + Scenario: As an unauthenticated user + Given I am not authenticated + When I do an API GET to content_assets.json + Then the JSON response at "error" should be "You need to sign in or sign up before continuing." + + # listing content assets + + Scenario: Accessing content assets as an Admin + Given I have an "admin" API token + When I do an API GET request to content_assets.json + Then the JSON response should be an array + And the JSON response should have 2 entries + + Scenario: Accessing content assets as a Designer + Given I have a "designer" API token + When I do an API GET request to content_assets.json + Then the JSON response should be an array + And the JSON response should have 2 entries + + Scenario: Accessing content assets as an Author + Given I have an "author" API token + When I do an API GET request to content_assets.json + Then the JSON response should be an array + And the JSON response should have 2 entries + + # showing content asset + + Scenario: Accessing content asset as an Admin + Given I have an "admin" API token + When I do an API GET request to content_assets/4f832c2cb0d86d3f42fffffe.json + Then the JSON response at "filename" should be "5k.png" + + Scenario: Accessing content asset as a Designer + Given I have a "designer" API token + When I do an API GET request to content_assets/4f832c2cb0d86d3f42fffffe.json + Then the JSON response at "filename" should be "5k.png" + + Scenario: Accessing content asset as an Author + Given I have an "author" API token + When I do an API GET request to content_assets/4f832c2cb0d86d3f42fffffe.json + Then the JSON response at "filename" should be "5k.png" + + # create content asset + + Scenario: Creating new content asset as an Admin + Given I have an "admin" API token + When I do an API GET request to content_assets.json + Then the JSON response should be an array + And the JSON response should have 2 entries + When I do a multipart API POST to content_assets.json with base key "content_asset" and: + | source | assets/application.js | + When I do an API GET request to content_assets.json + Then the JSON response should be an array + And the JSON response should have 3 entries + And the JSON at "2/filename" should be "application.js" + + Scenario: Creating new content asset as a Designer + Given I have a "designer" API token + When I do an API GET request to content_assets.json + Then the JSON response should be an array + And the JSON response should have 2 entries + When I do a multipart API POST to content_assets.json with base key "content_asset" and: + | source | assets/application.js | + When I do an API GET request to content_assets.json + Then the JSON response should be an array + And the JSON response should have 3 entries + And the JSON at "2/filename" should be "application.js" + + Scenario: Creating new content asset as an Author + Given I have an "author" API token + When I do an API GET request to content_assets.json + Then the JSON response should be an array + And the JSON response should have 2 entries + When I do a multipart API POST to content_assets.json with base key "content_asset" and: + | source | assets/application.js | + When I do an API GET request to content_assets.json + Then the JSON response should be an array + And the JSON response should have 3 entries + And the JSON at "2/filename" should be "application.js" + + # update content asset + + Scenario: Updating content asset as an Admin + Given I have an "admin" API token + When I do a multipart API PUT to content_assets/4f832c2cb0d86d3f42fffffe.json with base key "content_asset" and: + | source | assets/main.css | + When I do an API GET request to content_assets/4f832c2cb0d86d3f42fffffe.json + Then the JSON response at "filename" should be "main.css" + + Scenario: Updating content asset as a Designer + Given I have a "designer" API token + When I do a multipart API PUT to content_assets/4f832c2cb0d86d3f42fffffe.json with base key "content_asset" and: + | source | assets/main.css | + When I do an API GET request to content_assets/4f832c2cb0d86d3f42fffffe.json + Then the JSON response at "filename" should be "main.css" + + Scenario: Updating content asset as an Author + Given I have a "author" API token + When I do a multipart API PUT to content_assets/4f832c2cb0d86d3f42fffffe.json with base key "content_asset" and: + | source | assets/main.css | + When I do an API GET request to content_assets/4f832c2cb0d86d3f42fffffe.json + Then the JSON response at "filename" should be "main.css" + + # destroy content asset + + Scenario: Destroying content asset as an Admin + Given I have an "admin" API token + When I do an API GET request to content_assets.json + Then the JSON response should be an array + And the JSON response should have 2 entries + When I do an API DELETE to content_assets/4f832c2cb0d86d3f42fffffe.json + When I do an API GET request to content_assets.json + Then the JSON response should be an array + And the JSON response should have 1 entry + + Scenario: Destroying content asset as a Designer + Given I have a "designer" API token + When I do an API GET request to content_assets.json + Then the JSON response should be an array + And the JSON response should have 2 entries + When I do an API DELETE to content_assets/4f832c2cb0d86d3f42fffffe.json + When I do an API GET request to content_assets.json + Then the JSON response should be an array + And the JSON response should have 1 entry + + Scenario: Deleting content asset as an Author + Given I have a "author" API token + When I do an API GET request to content_assets.json + Then the JSON response should be an array + And the JSON response should have 2 entries + When I do an API DELETE to content_assets/4f832c2cb0d86d3f42fffffe.json + When I do an API GET request to content_assets.json + Then the JSON response should be an array + And the JSON response should have 1 entry diff --git a/features/api/authorization/content_entries.feature b/features/api/authorization/content_entries.feature new file mode 100644 index 00000000..af305298 --- /dev/null +++ b/features/api/authorization/content_entries.feature @@ -0,0 +1,202 @@ +Feature: Content Entries + In order to ensure content entries are not tampered with + As an admin, designer or author + I will be restricted based on my role + + Background: + Given I have the site: "test site" set up + And I have a custom model named "Projects" with + | label | type | required | + | Name | string | true | + | Description | text | false | + And I have entries for "Projects" with + | id | name | description | + | 4f832c2cb0d86d3f42fffffe | Project 1 | The first project | + | 4f832c2cb0d86d3f42ffffff | Project 2 | The second project | + And I have a designer and an author + + Scenario: As an unauthenticated user + Given I am not authenticated + When I do an API GET to content_types/projects/entries.json + Then the JSON response at "error" should be "You need to sign in or sign up before continuing." + + # listing content entries + + Scenario: Accessing content entries as an Admin + Given I have an "admin" API token + When I do an API GET request to content_types/projects/entries.json + Then the JSON response should be an array + And the JSON response should have 2 entries + + Scenario: Accessing content entries as a Designer + Given I have a "designer" API token + When I do an API GET request to content_types/projects/entries.json + Then the JSON response should be an array + And the JSON response should have 2 entries + + Scenario: Accessing content entries as an Author + Given I have an "author" API token + When I do an API GET request to content_types/projects/entries.json + Then the JSON response should be an array + And the JSON response should have 2 entries + + # showing content entry + + Scenario: Accessing content entry as an Admin + Given I have an "admin" API token + When I do an API GET request to content_types/projects/entries/4f832c2cb0d86d3f42fffffe.json + Then the JSON response at "id" should be "4f832c2cb0d86d3f42fffffe" + And the JSON response at "name" should be "Project 1" + + Scenario: Accessing content entry as a Designer + Given I have a "designer" API token + When I do an API GET request to content_types/projects/entries/4f832c2cb0d86d3f42fffffe.json + Then the JSON response at "id" should be "4f832c2cb0d86d3f42fffffe" + And the JSON response at "name" should be "Project 1" + + Scenario: Accessing content entry as an Author + Given I have an "author" API token + When I do an API GET request to content_types/projects/entries/4f832c2cb0d86d3f42fffffe.json + Then the JSON response at "id" should be "4f832c2cb0d86d3f42fffffe" + And the JSON response at "name" should be "Project 1" + + # create content entry + + Scenario: Creating new content entry as an Admin + Given I have an "admin" API token + When I do an API GET request to content_types/projects/entries.json + Then the JSON response should be an array + And the JSON response should have 2 entries + When I do an API POST to content_types/projects/entries.json with: + """ + { + "content_entry": { + "name": "Project 3", + "description": "The third..." + } + } + """ + When I do an API GET request to content_types/projects/entries.json + Then the JSON response should be an array + And the JSON response should have 3 entries + And the JSON should have the following: + | 2/name | "Project 3" | + | 2/description | "The third..." | + + Scenario: Creating new content entry as a Designer + Given I have a "designer" API token + When I do an API GET request to content_types/projects/entries.json + Then the JSON response should be an array + And the JSON response should have 2 entries + When I do an API POST to content_types/projects/entries.json with: + """ + { + "content_entry": { + "name": "Project 3", + "description": "The third..." + } + } + """ + When I do an API GET request to content_types/projects/entries.json + Then the JSON response should be an array + And the JSON response should have 3 entries + And the JSON should have the following: + | 2/name | "Project 3" | + | 2/description | "The third..." | + + Scenario: Creating new content entry as an Author + Given I have an "author" API token + When I do an API GET request to content_types/projects/entries.json + Then the JSON response should be an array + And the JSON response should have 2 entries + When I do an API POST to content_types/projects/entries.json with: + """ + { + "content_entry": { + "name": "Project 3", + "description": "The third..." + } + } + """ + When I do an API GET request to content_types/projects/entries.json + Then the JSON response should be an array + And the JSON response should have 3 entries + And the JSON should have the following: + | 2/name | "Project 3" | + | 2/description | "The third..." | + + # update content entry + + Scenario: Updating content entry as an Admin + Given I have an "admin" API token + When I do an API PUT to content_types/projects/entries/4f832c2cb0d86d3f42fffffe.json with: + """ + { + "content_entry": { + "description": "The awesomest project ever!" + } + } + """ + When I do an API GET request to content_types/projects/entries/4f832c2cb0d86d3f42fffffe.json + Then the JSON response at "name" should be "Project 1" + And the JSON response at "description" should be "The awesomest project ever!" + + Scenario: Updating content entry as a Designer + Given I have a "designer" API token + When I do an API PUT to content_types/projects/entries/4f832c2cb0d86d3f42fffffe.json with: + """ + { + "content_entry": { + "description": "The awesomest project ever!" + } + } + """ + When I do an API GET request to content_types/projects/entries/4f832c2cb0d86d3f42fffffe.json + Then the JSON response at "name" should be "Project 1" + And the JSON response at "description" should be "The awesomest project ever!" + + Scenario: Updating content entry as an Author + Given I have a "author" API token + When I do an API PUT to content_types/projects/entries/4f832c2cb0d86d3f42fffffe.json with: + """ + { + "content_entry": { + "description": "The awesomest project ever!" + } + } + """ + When I do an API GET request to content_types/projects/entries/4f832c2cb0d86d3f42fffffe.json + Then the JSON response at "name" should be "Project 1" + And the JSON response at "description" should be "The awesomest project ever!" + + # destroy content entry + + Scenario: Destroying content entry as an Admin + Given I have an "admin" API token + When I do an API GET request to content_types/projects/entries.json + Then the JSON response should be an array + And the JSON response should have 2 entries + When I do an API DELETE to content_types/projects/entries/4f832c2cb0d86d3f42fffffe.json + When I do an API GET request to content_types/projects/entries.json + Then the JSON response should be an array + And the JSON response should have 1 entry + + Scenario: Destroying content entry as a Designer + Given I have a "designer" API token + When I do an API GET request to content_types/projects/entries.json + Then the JSON response should be an array + And the JSON response should have 2 entries + When I do an API DELETE to content_types/projects/entries/4f832c2cb0d86d3f42fffffe.json + When I do an API GET request to content_types/projects/entries.json + Then the JSON response should be an array + And the JSON response should have 1 entry + + Scenario: Deleting content entry as an Author + Given I have a "author" API token + When I do an API GET request to content_types/projects/entries.json + Then the JSON response should be an array + And the JSON response should have 2 entries + When I do an API DELETE to content_types/projects/entries/4f832c2cb0d86d3f42fffffe.json + When I do an API GET request to content_types/projects/entries.json + Then the JSON response should be an array + And the JSON response should have 1 entry diff --git a/features/api/authorization/content_types.feature b/features/api/authorization/content_types.feature new file mode 100644 index 00000000..0012d91a --- /dev/null +++ b/features/api/authorization/content_types.feature @@ -0,0 +1,237 @@ +Feature: Content Types + In order to ensure content types are not tampered with + As an admin, designer or author + I will be restricted based on my role + + Background: + Given I have the site: "test site" set up + And I have a custom model named "Projects" with id "4f832c2cb0d86d3f42fffffe" and + | label | type | required | + | Name | string | true | + | Description | text | false | + And I have a designer and an author + + Scenario: As an unauthenticated user + Given I am not authenticated + When I do an API GET to content_types.json + Then the JSON response at "error" should be "You need to sign in or sign up before continuing." + + # listing content types + + Scenario: Accessing content types as an Admin + Given I have an "admin" API token + When I do an API GET request to content_types.json + Then the JSON response should be an array + And the JSON response should have 1 entry + + Scenario: Accessing content types as a Designer + Given I have a "designer" API token + When I do an API GET request to content_types.json + Then the JSON response should be an array + And the JSON response should have 1 entry + + Scenario: Accessing content types as an Author + Given I have an "author" API token + When I do an API GET request to content_types.json + Then the JSON response should be an array + And the JSON response should have 1 entry + + # showing content type + + Scenario: Accessing content type as an Admin + Given I have an "admin" API token + When I do an API GET request to content_types/4f832c2cb0d86d3f42fffffe.json + Then the JSON response at "id" should be "4f832c2cb0d86d3f42fffffe" + And the JSON response at "name" should be "Projects" + + Scenario: Accessing content type as a Designer + Given I have a "designer" API token + When I do an API GET request to content_types/4f832c2cb0d86d3f42fffffe.json + Then the JSON response at "id" should be "4f832c2cb0d86d3f42fffffe" + And the JSON response at "name" should be "Projects" + + Scenario: Accessing content type as an Author + Given I have an "author" API token + When I do an API GET request to content_types/4f832c2cb0d86d3f42fffffe.json + Then the JSON response at "id" should be "4f832c2cb0d86d3f42fffffe" + And the JSON response at "name" should be "Projects" + + # create content type + + Scenario: Creating new content type as an Admin + Given I have an "admin" API token + When I do an API GET request to content_types.json + Then the JSON response should be an array + And the JSON response should have 1 entry + When I do an API POST to content_types.json with: + """ + { + "content_type": { + "name": "Employees", + "slug": "employees", + "entries_custom_fields": [ + { + "label": "Name", + "name": "name", + "type": "string" + }, + { + "label": "Position", + "name": "position", + "type": "string" + } + ] + } + } + """ + When I do an API GET request to content_types.json + Then the JSON response should be an array + And the JSON response should have 2 entries + And the JSON should have the following: + | 1/name | "Employees" | + | 1/slug | "employees" | + | 1/entries_custom_fields/0/label | "Name" | + | 1/entries_custom_fields/0/name | "name" | + | 1/entries_custom_fields/0/type | "string" | + | 1/entries_custom_fields/1/label | "Position" | + | 1/entries_custom_fields/1/name | "position" | + | 1/entries_custom_fields/1/type | "string" | + + Scenario: Creating new content type as a Designer + Given I have a "designer" API token + When I do an API GET request to content_types.json + Then the JSON response should be an array + And the JSON response should have 1 entry + When I do an API POST to content_types.json with: + """ + { + "content_type": { + "name": "Employees", + "slug": "employees", + "entries_custom_fields": [ + { + "label": "Name", + "name": "name", + "type": "string" + }, + { + "label": "Position", + "name": "position", + "type": "string" + } + ] + } + } + """ + When I do an API GET request to content_types.json + Then the JSON response should be an array + And the JSON response should have 2 entries + And the JSON should have the following: + | 1/name | "Employees" | + | 1/slug | "employees" | + | 1/entries_custom_fields/0/label | "Name" | + | 1/entries_custom_fields/0/name | "name" | + | 1/entries_custom_fields/0/type | "string" | + | 1/entries_custom_fields/1/label | "Position" | + | 1/entries_custom_fields/1/name | "position" | + | 1/entries_custom_fields/1/type | "string" | + + Scenario: Creating new content type as an Author + Given I have an "author" API token + When I do an API GET request to content_types.json + Then the JSON response should be an array + And the JSON response should have 1 entry + When I do an API POST to content_types.json with: + """ + { + "content_type": { + "name": "Employees", + "slug": "employees", + "entries_custom_fields": [ + { + "label": "Name", + "name": "name", + "type": "string" + }, + { + "label": "Position", + "name": "position", + "type": "string" + } + ] + } + } + """ + Then an access denied error should occur + + # update content type + + Scenario: Updating content type as an Admin + Given I have an "admin" API token + When I do an API PUT to content_types/4f832c2cb0d86d3f42fffffe.json with: + """ + { + "content_type": { + "name": "Brand new updated name" + } + } + """ + When I do an API GET request to content_types/4f832c2cb0d86d3f42fffffe.json + Then the JSON response at "id" should be "4f832c2cb0d86d3f42fffffe" + And the JSON response at "name" should be "Brand new updated name" + + Scenario: Updating content type as a Designer + Given I have a "designer" API token + When I do an API PUT to content_types/4f832c2cb0d86d3f42fffffe.json with: + """ + { + "content_type": { + "name": "Brand new updated name" + } + } + """ + When I do an API GET request to content_types/4f832c2cb0d86d3f42fffffe.json + Then the JSON response at "id" should be "4f832c2cb0d86d3f42fffffe" + And the JSON response at "name" should be "Brand new updated name" + + Scenario: Updating content type as an Author + Given I have a "author" API token + When I do an API PUT to content_types/4f832c2cb0d86d3f42fffffe.json with: + """ + { + "content_type": { + "name": "Brand new updated name" + } + } + """ + Then an access denied error should occur + + # destroy content type + + Scenario: Destroying content type as an Admin + Given I have an "admin" API token + When I do an API GET request to content_types.json + Then the JSON response should be an array + And the JSON response should have 1 entry + When I do an API DELETE to content_types/4f832c2cb0d86d3f42fffffe.json + When I do an API GET request to content_types.json + Then the JSON response should be an array + And the JSON response should have 0 entries + + Scenario: Destroying content type as a Designer + Given I have a "designer" API token + When I do an API GET request to content_types.json + Then the JSON response should be an array + And the JSON response should have 1 entry + When I do an API DELETE to content_types/4f832c2cb0d86d3f42fffffe.json + When I do an API GET request to content_types.json + Then the JSON response should be an array + And the JSON response should have 0 entries + + Scenario: Deleting content type as an Author + Given I have a "author" API token + When I do an API GET request to content_types.json + Then the JSON response should be an array + And the JSON response should have 1 entries + When I do an API DELETE to content_types/4f832c2cb0d86d3f42fffffe.json + Then an access denied error should occur diff --git a/features/api/authorization/current_site.feature b/features/api/authorization/current_site.feature new file mode 100644 index 00000000..8a61a372 --- /dev/null +++ b/features/api/authorization/current_site.feature @@ -0,0 +1,30 @@ +Feature: Current Site + In order to ensure the current site can be viewed by all authenticated users + As an admin, designer or author + I should be able to show the current site + + Background: + Given I have the site: "test site" set up + And I have a designer and an author + + Scenario: As an unauthenticated user + Given I am not authenticated + When I do an API GET to current_site.json + Then the JSON response at "error" should be "You need to sign in or sign up before continuing." + + # showing current site + + Scenario: Accessing current site as an Admin + Given I have an "admin" API token + When I do an API GET to current_site.json + Then the JSON response at "name" should be "Locomotive test website" + + Scenario: Accessing current site as a Designer + Given I have a "designer" API token + When I do an API GET to current_site.json + Then the JSON response at "name" should be "Locomotive test website" + + Scenario: Accessing current site as an Author + Given I have an "author" API token + When I do an API GET to current_site.json + Then the JSON response at "name" should be "Locomotive test website" diff --git a/features/api/authorization/memberships.feature b/features/api/authorization/memberships.feature new file mode 100644 index 00000000..35663c50 --- /dev/null +++ b/features/api/authorization/memberships.feature @@ -0,0 +1,225 @@ +Feature: Memberships + In order to ensure memberships are not tampered with + As an admin, designer or author + I will be restricted based on my role + + Background: + Given I have the site: "test site" set up with id: "4f832c2cb0d86d3f42fffffb" + And I have accounts: + | email | id | + | new-user@a.com | 4f832c2cb0d86d3f42fffffc | + And I have memberships: + | email | role | id | + | admin@a.com | admin | 4f832c2cb0d86d3f42fffffd | + | designer@a.com | designer | 4f832c2cb0d86d3f42fffffe | + | author@a.com | author | 4f832c2cb0d86d3f42ffffff | + + Scenario: As an unauthenticated user + Given I am not authenticated + When I do an API GET to memberships.json + Then the JSON response at "error" should be "You need to sign in or sign up before continuing." + + # listing memberships + + Scenario: Accessing memberships as an Admin + Given I have an "admin" API token + When I do an API GET request to memberships.json + Then the JSON response should be an array + And the JSON response should have 4 entries + + Scenario: Accessing memberships as a Designer + Given I have a "designer" API token + When I do an API GET request to memberships.json + Then the JSON response should be an array + And the JSON response should have 4 entries + + Scenario: Accessing memberships as an Author + Given I have an "author" API token + When I do an API GET request to memberships.json + Then an access denied error should occur + + # showing membership + + Scenario: Accessing membership as an Admin + Given I have an "admin" API token + When I do an API GET request to memberships/4f832c2cb0d86d3f42fffffd.json + Then the JSON response at "email" should be "admin@a.com" + When I do an API GET request to memberships/4f832c2cb0d86d3f42fffffe.json + Then the JSON response at "email" should be "designer@a.com" + When I do an API GET request to memberships/4f832c2cb0d86d3f42ffffff.json + Then the JSON response at "email" should be "author@a.com" + + Scenario: Accessing membership as a Designer + Given I have a "designer" API token + When I do an API GET request to memberships/4f832c2cb0d86d3f42fffffd.json + Then the JSON response at "email" should be "admin@a.com" + When I do an API GET request to memberships/4f832c2cb0d86d3f42fffffe.json + Then the JSON response at "email" should be "designer@a.com" + When I do an API GET request to memberships/4f832c2cb0d86d3f42ffffff.json + Then the JSON response at "email" should be "author@a.com" + + Scenario: Accessing membership as an Author + Given I have an "author" API token + When I do an API GET request to memberships/4f832c2cb0d86d3f42fffffe.json + Then an access denied error should occur + + # create membership + + Scenario: Creating new membership as an Admin + Given I have an "admin" API token + When I do an API POST to memberships.json with: + """ + { + "membership": { + "site_id": "4f832c2cb0d86d3f42fffffb", + "account_id": "4f832c2cb0d86d3f42fffffc" + } + } + """ + When I do an API GET request to memberships.json + Then the JSON response should be an array + And the JSON response should have 5 entries + + Scenario: Creating new membership as a Designer + Given I have a "designer" API token + When I do an API POST to memberships.json with: + """ + { + "membership": { + "site_id": "4f832c2cb0d86d3f42fffffb", + "account_id": "4f832c2cb0d86d3f42fffffc" + } + } + """ + When I do an API GET request to memberships.json + Then the JSON response should be an array + And the JSON response should have 5 entries + + Scenario: Creating new membership as an Author + Given I have an "author" API token + When I do an API POST to memberships.json with: + """ + { + "membership": { + "site_id": "4f832c2cb0d86d3f42fffffb", + "account_id": "4f832c2cb0d86d3f42fffffc" + } + } + """ + Then an access denied error should occur + + Scenario: Created membership should always be Author + Given I have an "admin" API token + When I do an API POST to memberships.json with: + """ + { + "membership": { + "site_id": "4f832c2cb0d86d3f42fffffb", + "account_id": "4f832c2cb0d86d3f42fffffc", + "role": "admin" + } + } + """ + When I do an API GET request to memberships.json + Then the JSON response should be an array + And the JSON response should have 5 entries + And the JSON at "4/role" should be "author" + + # update membership + + Scenario: Updating membership as an Admin + Given I have an "admin" API token + When I do an API PUT to memberships/4f832c2cb0d86d3f42ffffff.json with: + """ + { + "membership": { + "role": "admin" + } + } + """ + When I do an API GET request to memberships/4f832c2cb0d86d3f42ffffff.json + Then the JSON response at "role" should be "admin" + + Scenario: Updating membership as a Designer + Given I have a "designer" API token + When I do an API PUT to memberships/4f832c2cb0d86d3f42ffffff.json with: + """ + { + "membership": { + "role": "admin" + } + } + """ + When I do an API GET request to memberships/4f832c2cb0d86d3f42ffffff.json + Then the JSON response at "role" should be "author" + When I do an API PUT to memberships/4f832c2cb0d86d3f42ffffff.json with: + """ + { + "membership": { + "role": "designer" + } + } + """ + When I do an API GET request to memberships/4f832c2cb0d86d3f42ffffff.json + Then the JSON response at "role" should be "designer" + + Scenario: Updating membership as an Author + Given I have a "author" API token + When I do an API PUT to memberships/4f832c2cb0d86d3f42ffffff.json with: + """ + { + "membership": { + "role": "admin" + } + } + """ + Then an access denied error should occur + When I do an API PUT to memberships/4f832c2cb0d86d3f42ffffff.json with: + """ + { + "membership": { + "role": "designer" + } + } + """ + Then an access denied error should occur + When I do an API PUT to memberships/4f832c2cb0d86d3f42ffffff.json with: + """ + { + "membership": { + "role": "author" + } + } + """ + Then an access denied error should occur + + # destroy membership + + Scenario: Destroying membership as an Admin + Given I have an "admin" API token + When I do an API GET request to memberships.json + Then the JSON response should be an array + And the JSON response should have 4 entries + When I do an API DELETE to memberships/4f832c2cb0d86d3f42ffffff.json + When I do an API GET request to memberships.json + Then the JSON response should be an array + And the JSON response should have 3 entries + + Scenario: Destroying membership as a Designer + Given I have a "designer" API token + When I do an API GET request to memberships.json + Then the JSON response should be an array + And the JSON response should have 4 entries + When I do an API DELETE to memberships/4f832c2cb0d86d3f42ffffff.json + When I do an API GET request to memberships.json + Then the JSON response should be an array + And the JSON response should have 3 entries + When I do an API DELETE to memberships/4f832c2cb0d86d3f42fffffe.json + Then an access denied error should occur + When I do an API DELETE to memberships/4f832c2cb0d86d3f42fffffd.json + Then an access denied error should occur + + Scenario: Deleting membership as an Author + Given I have a "author" API token + When I do an API DELETE to memberships/4f832c2cb0d86d3f42fffffe.json + Then an access denied error should occur diff --git a/features/api/authorization/pages.feature b/features/api/authorization/pages.feature new file mode 100644 index 00000000..2cc7d855 --- /dev/null +++ b/features/api/authorization/pages.feature @@ -0,0 +1,187 @@ +Feature: Pages + In order to ensure pages are not tampered with + As an admin, designer or author + I will be restricted based on my role + + Background: + Given I have the site: "test site" set up + And I have a custom model named "Projects" with + | label | type | required | + | Name | string | true | + | Description | text | false | + And I have a designer and an author + And a page named "hello-world" with id "4f832c2cb0d86d3f42fffffe" + And a page named "goodbye-world" with id "4f832c2cb0d86d3f42ffffff" + + Scenario: As an unauthenticated user + Given I am not authenticated + When I do an API GET to pages.json + Then the JSON response at "error" should be "You need to sign in or sign up before continuing." + + # listing pages + + Scenario: Accessing pages as an Admin + Given I have an "admin" API token + When I do an API GET request to pages.json + Then the JSON response should be an array + And the JSON response should have 4 entries + + Scenario: Accessing pages as a Designer + Given I have a "designer" API token + When I do an API GET request to pages.json + Then the JSON response should be an array + And the JSON response should have 4 entries + + Scenario: Accessing pages as an Author + Given I have an "author" API token + When I do an API GET request to pages.json + Then the JSON response should be an array + And the JSON response should have 4 entries + + # showing page + + Scenario: Accessing page as an Admin + Given I have an "admin" API token + When I do an API GET request to pages/4f832c2cb0d86d3f42fffffe.json + Then the JSON response at "id" should be "4f832c2cb0d86d3f42fffffe" + And the JSON response at "slug" should be "hello-world" + + Scenario: Accessing page as a Designer + Given I have a "designer" API token + When I do an API GET request to pages/4f832c2cb0d86d3f42fffffe.json + Then the JSON response at "id" should be "4f832c2cb0d86d3f42fffffe" + And the JSON response at "slug" should be "hello-world" + + Scenario: Accessing page as an Author + Given I have an "author" API token + When I do an API GET request to pages/4f832c2cb0d86d3f42fffffe.json + Then the JSON response at "id" should be "4f832c2cb0d86d3f42fffffe" + And the JSON response at "slug" should be "hello-world" + + # create page + + Scenario: Creating new page as an Admin + Given I have an "admin" API token + When I do an API GET request to pages.json + Then the JSON response should be an array + And the JSON response should have 4 entries + When I do an API POST to pages.json with: + """ + { + "page": { + "title": "New Page", + "slug": "new-page", + "parent_id": "4f832c2cb0d86d3f42fffffe" + } + } + """ + When I do an API GET request to pages.json + Then the JSON response should be an array + And the JSON response should have 5 entries + + Scenario: Creating new page as a Designer + Given I have a "designer" API token + When I do an API GET request to pages.json + Then the JSON response should be an array + And the JSON response should have 4 entries + When I do an API POST to pages.json with: + """ + { + "page": { + "title": "New Page", + "slug": "new-page", + "parent_id": "4f832c2cb0d86d3f42fffffe" + } + } + """ + When I do an API GET request to pages.json + Then the JSON response should be an array + And the JSON response should have 5 entries + + Scenario: Creating new page as an Author + Given I have an "author" API token + When I do an API POST to pages.json with: + """ + { + "page": { + "title": "New Page", + "slug": "new-page", + "parent_id": "4f832c2cb0d86d3f42fffffe" + } + } + """ + Then an access denied error should occur + + # update page + + Scenario: Updating page as an Admin + Given I have an "admin" API token + When I do an API PUT to pages/4f832c2cb0d86d3f42fffffe.json with: + """ + { + "page": { + "title": "Brand new updated title" + } + } + """ + When I do an API GET request to pages/4f832c2cb0d86d3f42fffffe.json + Then the JSON response at "id" should be "4f832c2cb0d86d3f42fffffe" + And the JSON response at "title" should be "Brand new updated title" + + Scenario: Updating page as a Designer + Given I have a "designer" API token + When I do an API PUT to pages/4f832c2cb0d86d3f42fffffe.json with: + """ + { + "page": { + "title": "Brand new updated title" + } + } + """ + When I do an API GET request to pages/4f832c2cb0d86d3f42fffffe.json + Then the JSON response at "id" should be "4f832c2cb0d86d3f42fffffe" + And the JSON response at "title" should be "Brand new updated title" + + Scenario: Updating page as an Author + Given I have a "author" API token + When I do an API PUT to pages/4f832c2cb0d86d3f42fffffe.json with: + """ + { + "page": { + "title": "Brand new updated title" + } + } + """ + When I do an API GET request to pages/4f832c2cb0d86d3f42fffffe.json + Then the JSON response at "id" should be "4f832c2cb0d86d3f42fffffe" + And the JSON response at "title" should be "Brand new updated title" + + # destroy page + + Scenario: Destroying page as an Admin + Given I have an "admin" API token + When I do an API GET request to pages.json + Then the JSON response should be an array + And the JSON response should have 4 entries + When I do an API DELETE to pages/4f832c2cb0d86d3f42fffffe.json + When I do an API GET request to pages.json + Then the JSON response should be an array + And the JSON response should have 3 entries + + Scenario: Destroying page as a Designer + Given I have a "designer" API token + When I do an API GET request to pages.json + Then the JSON response should be an array + And the JSON response should have 4 entries + When I do an API DELETE to pages/4f832c2cb0d86d3f42fffffe.json + When I do an API GET request to pages.json + Then the JSON response should be an array + And the JSON response should have 3 entries + + Scenario: Deleting page as an Author + Given I have a "author" API token + When I do an API GET request to pages.json + Then the JSON response should be an array + And the JSON response should have 4 entries + When I do an API DELETE to pages/4f832c2cb0d86d3f42fffffe.json + Then an access denied error should occur diff --git a/features/api/authorization/sites.feature b/features/api/authorization/sites.feature new file mode 100644 index 00000000..bb7b393a --- /dev/null +++ b/features/api/authorization/sites.feature @@ -0,0 +1,206 @@ +Feature: Sites + In order to ensure sites are not tampered with + As an admin, designer or author + I will be restricted based on my role + + Background: + Given I have the site: "test site" set up with id: "4f832c2cb0d86d3f42fffffe" + And I have the site: "another site" set up with id: "4f832c2cb0d86d3f42ffffff" + And I have a designer and an author + + Scenario: As an unauthenticated user + Given I am not authenticated + When I do an API GET to sites.json + Then the JSON response at "error" should be "You need to sign in or sign up before continuing." + + # listing sites + + Scenario: Accessing sites as an Admin + Given I have an "admin" API token + When I do an API GET request to sites.json + Then the JSON response should be an array + And the JSON response should have 2 entries + + Scenario: Accessing sites as a Designer + Given I have a "designer" API token + When I do an API GET request to sites.json + Then an access denied error should occur + + Scenario: Accessing sites as an Author + Given I have an "author" API token + When I do an API GET request to sites.json + Then an access denied error should occur + + # showing site + + Scenario: Accessing site as an Admin + Given I have an "admin" API token + When I do an API GET request to sites/4f832c2cb0d86d3f42fffffe.json + Then the JSON response at "name" should be "Locomotive test website" + + Scenario: Accessing my site as a Designer + Given I have a "designer" API token + When I do an API GET request to sites/4f832c2cb0d86d3f42fffffe.json + Then the JSON response at "name" should be "Locomotive test website" + + Scenario: Accessing other site as a Designer + Given I have a "designer" API token + When I do an API GET request to sites/4f832c2cb0d86d3f42ffffff.json + Then an access denied error should occur + + Scenario: Accessing my site as an Author + Given I have an "author" API token + When I do an API GET request to sites/4f832c2cb0d86d3f42fffffe.json + Then the JSON response at "name" should be "Locomotive test website" + + Scenario: Accessing other site as an Author + Given I have an "author" API token + When I do an API GET request to sites/4f832c2cb0d86d3f42ffffff.json + Then an access denied error should occur + + # create site + + Scenario: Creating new site as an Admin + Given I have an "admin" API token + When I do an API GET request to sites.json + Then the JSON response should be an array + And the JSON response should have 2 entries + When I do an API POST to sites.json with: + """ + { + "site": { + "name": "New site", + "subdomain": "new-site" + } + } + """ + When I do an API GET request to sites.json + Then the JSON response should be an array + And the JSON response should have 3 entries + + Scenario: Creating new site as a Designer + Given I have a "designer" API token + When I do an API POST to sites.json with: + """ + { + "site": { + "name": "New site", + "subdomain": "new-site" + } + } + """ + Then an access denied error should occur + + Scenario: Creating new site as an Author + Given I have an "author" API token + When I do an API POST to sites.json with: + """ + { + "site": { + "name": "New site", + "subdomain": "new-site" + } + } + """ + Then an access denied error should occur + + # update site + + Scenario: Updating site as an Admin + Given I have an "admin" API token + When I do an API PUT to sites/4f832c2cb0d86d3f42fffffe.json with: + """ + { + "site": { + "name": "Brand new updated name" + } + } + """ + When I do an API GET request to sites/4f832c2cb0d86d3f42fffffe.json + Then the JSON response at "id" should be "4f832c2cb0d86d3f42fffffe" + And the JSON response at "name" should be "Brand new updated name" + + Scenario: Updating my site as a Designer + Given I have a "designer" API token + When I do an API PUT to sites/4f832c2cb0d86d3f42fffffe.json with: + """ + { + "site": { + "name": "Brand new updated name" + } + } + """ + When I do an API GET request to sites/4f832c2cb0d86d3f42fffffe.json + Then the JSON response at "id" should be "4f832c2cb0d86d3f42fffffe" + And the JSON response at "name" should be "Brand new updated name" + + Scenario: Updating other site as a Designer + Given I have a "designer" API token + When I do an API PUT to sites/4f832c2cb0d86d3f42ffffff.json with: + """ + { + "site": { + "name": "Brand new updated name" + } + } + """ + Then an access denied error should occur + + Scenario: Updating my site as an Author + Given I have a "author" API token + When I do an API PUT to sites/4f832c2cb0d86d3f42fffffe.json with: + """ + { + "site": { + "name": "Brand new updated name" + } + } + """ + When I do an API GET request to sites/4f832c2cb0d86d3f42fffffe.json + Then the JSON response at "id" should be "4f832c2cb0d86d3f42fffffe" + And the JSON response at "name" should be "Brand new updated name" + + Scenario: Updating other site as an Author + Given I have a "author" API token + When I do an API PUT to sites/4f832c2cb0d86d3f42ffffff.json with: + """ + { + "site": { + "name": "Brand new updated name" + } + } + """ + Then an access denied error should occur + + # destroy site + + Scenario: Destroying site as an Admin + Given I have an "admin" API token + When I do an API GET request to sites.json + Then the JSON response should be an array + And the JSON response should have 2 entries + When I do an API DELETE to sites/4f832c2cb0d86d3f42fffffe.json + When I do an API GET request to sites.json + Then the JSON response should be an array + And the JSON response should have 1 entries + + Scenario: Destroying my site as a Designer + Given I have a "designer" API token + When I do an API DELETE to sites/4f832c2cb0d86d3f42fffffe.json + When I do an API GET request to sites/4f832c2cb0d86d3f42fffffe.json + Then it should not exist + + Scenario: Deleting other site as a Designer + Given I have a "designer" API token + When I do an API DELETE to sites/4f832c2cb0d86d3f42ffffff.json + Then an access denied error should occur + + Scenario: Deleting my site as an Author + Given I have a "author" API token + When I do an API DELETE to sites/4f832c2cb0d86d3f42fffffe.json + Then an access denied error should occur + + Scenario: Deleting other site as an Author + Given I have a "author" API token + When I do an API DELETE to sites/4f832c2cb0d86d3f42ffffff.json + Then an access denied error should occur diff --git a/features/api/authorization/snippets.feature b/features/api/authorization/snippets.feature new file mode 100644 index 00000000..058ca7e1 --- /dev/null +++ b/features/api/authorization/snippets.feature @@ -0,0 +1,179 @@ +Feature: Snippets + In order to ensure snippets are not tampered with + As an admin, designer or author + I will be restricted based on my role + + Background: + Given I have the site: "test site" set up + And a snippet named "My Snippet" with id "4f832c2cb0d86d3f42fffffe" and template: + """ + My Snippet + """ + And I have a designer and an author + + Scenario: As an unauthenticated user + Given I am not authenticated + When I do an API GET to snippets.json + Then the JSON response at "error" should be "You need to sign in or sign up before continuing." + + # listing content types + + Scenario: Accessing snippets as an Admin + Given I have an "admin" API token + When I do an API GET request to snippets.json + Then the JSON response should be an array + And the JSON response should have 1 entry + + Scenario: Accessing snippets as a Designer + Given I have a "designer" API token + When I do an API GET request to snippets.json + Then the JSON response should be an array + And the JSON response should have 1 entry + + Scenario: Accessing snippets as an Author + Given I have an "author" API token + When I do an API GET request to snippets.json + Then an access denied error should occur + + # showing snippet + + Scenario: Accessing snippet as an Admin + Given I have an "admin" API token + When I do an API GET request to snippets/4f832c2cb0d86d3f42fffffe.json + Then the JSON response at "id" should be "4f832c2cb0d86d3f42fffffe" + And the JSON response at "name" should be "My Snippet" + + Scenario: Accessing snippet as a Designer + Given I have a "designer" API token + When I do an API GET request to snippets/4f832c2cb0d86d3f42fffffe.json + Then the JSON response at "id" should be "4f832c2cb0d86d3f42fffffe" + And the JSON response at "name" should be "My Snippet" + + Scenario: Accessing snippet as an Author + Given I have an "author" API token + When I do an API GET request to snippets/4f832c2cb0d86d3f42fffffe.json + Then an access denied error should occur + + # create snippet + + Scenario: Creating new snippet as an Admin + Given I have an "admin" API token + When I do an API GET request to snippets.json + Then the JSON response should be an array + And the JSON response should have 1 entry + When I do an API POST to snippets.json with: + """ + { + "snippet": { + "name": "Another snippet", + "template": "