From 0512b5fec63494589f90ac09e117333be834a4fb Mon Sep 17 00:00:00 2001 From: jacobherrington Date: Mon, 16 Oct 2017 16:24:40 -0500 Subject: [PATCH 1/4] add pundit --- Gemfile | 3 +- Gemfile.lock | 3 ++ app/controllers/application_controller.rb | 1 + app/models/assignment.rb | 4 ++ app/models/role.rb | 6 +++ app/models/user.rb | 7 +++ app/policies/application_policy.rb | 53 +++++++++++++++++++ db/migrate/20171016210855_create_roles.rb | 9 ++++ .../20171016211209_create_assignments.rb | 10 ++++ db/schema.rb | 27 +++++++++- spec/factories/assignments.rb | 6 +++ spec/factories/roles.rb | 5 ++ spec/models/assignment_spec.rb | 5 ++ spec/models/role_spec.rb | 5 ++ 14 files changed, 142 insertions(+), 2 deletions(-) create mode 100644 app/models/assignment.rb create mode 100644 app/models/role.rb create mode 100644 app/policies/application_policy.rb create mode 100644 db/migrate/20171016210855_create_roles.rb create mode 100644 db/migrate/20171016211209_create_assignments.rb create mode 100644 spec/factories/assignments.rb create mode 100644 spec/factories/roles.rb create mode 100644 spec/models/assignment_spec.rb create mode 100644 spec/models/role_spec.rb diff --git a/Gemfile b/Gemfile index 583d1d8..6b2dcee 100644 --- a/Gemfile +++ b/Gemfile @@ -34,8 +34,9 @@ gem 'jquery-rails' # Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder gem 'jbuilder', '~> 2.5' -# User authentication +# User authentication and roles gem 'devise', '~> 4.3' +gem 'pundit' # Geocoding addresses to lat/long gem 'geocoder', '~> 1.4' diff --git a/Gemfile.lock b/Gemfile.lock index 43f613c..2f031bd 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -158,6 +158,8 @@ GEM pry (>= 0.10.4) public_suffix (2.0.5) puma (3.8.2) + pundit (1.1.0) + activesupport (>= 3.0.0) rack (2.0.2) rack-test (0.6.3) rack (>= 1.0) @@ -298,6 +300,7 @@ DEPENDENCIES pry-byebug pry-rails puma (~> 3.7) + pundit rails (~> 5.1.1) refills rspec-rails diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 10b066d..3f2350b 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -1,4 +1,5 @@ class ApplicationController < ActionController::Base + include Pundit protect_from_forgery with: :exception before_action :configure_permitted_parameters, if: :devise_controller? diff --git a/app/models/assignment.rb b/app/models/assignment.rb new file mode 100644 index 0000000..ef4df5a --- /dev/null +++ b/app/models/assignment.rb @@ -0,0 +1,4 @@ +class Assignment < ApplicationRecord + belongs_to :user + belongs_to :role +end diff --git a/app/models/role.rb b/app/models/role.rb new file mode 100644 index 0000000..e088311 --- /dev/null +++ b/app/models/role.rb @@ -0,0 +1,6 @@ +class Role < ApplicationRecord + has_many :assignments + has_many :users, through: :assignments + + validates :name, presence: true, uniqueness: true +end \ No newline at end of file diff --git a/app/models/user.rb b/app/models/user.rb index 3854129..0c78182 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -45,6 +45,9 @@ class User < ApplicationRecord has_many :organization_users has_many :organizations, through: :organization_users + has_many :assignments + has_many :roles, through: :assignments + geocoded_by :full_street_address after_validation :geocode, if: ->(user){ user.full_street_address.present? and user.full_street_address_changed? } @@ -52,6 +55,10 @@ def name "#{first_name} #{last_name}" end + def role?(role) + roles.any? { |r| r.name.underscore.to_sym == role } + end + # TODO: Add support for multiple interests scope :search_by_interest, (lambda { |interest| joins(:interests).merge(Interest.where("interest ILIKE ?", interest)) if interest.present? }) diff --git a/app/policies/application_policy.rb b/app/policies/application_policy.rb new file mode 100644 index 0000000..2a0bbc5 --- /dev/null +++ b/app/policies/application_policy.rb @@ -0,0 +1,53 @@ +class ApplicationPolicy + attr_reader :user, :record + + def initialize(user, record) + @user = user + @record = record + end + + def index? + false + end + + def show? + scope.where(:id => record.id).exists? + end + + def create? + false + end + + def new? + create? + end + + def update? + false + end + + def edit? + update? + end + + def destroy? + false + end + + def scope + Pundit.policy_scope!(user, record.class) + end + + class Scope + attr_reader :user, :scope + + def initialize(user, scope) + @user = user + @scope = scope + end + + def resolve + scope + end + end +end diff --git a/db/migrate/20171016210855_create_roles.rb b/db/migrate/20171016210855_create_roles.rb new file mode 100644 index 0000000..0d91ad3 --- /dev/null +++ b/db/migrate/20171016210855_create_roles.rb @@ -0,0 +1,9 @@ +class CreateRoles < ActiveRecord::Migration[5.1] + def change + create_table :roles do |t| + t.string :name + + t.timestamps + end + end +end diff --git a/db/migrate/20171016211209_create_assignments.rb b/db/migrate/20171016211209_create_assignments.rb new file mode 100644 index 0000000..b0deeeb --- /dev/null +++ b/db/migrate/20171016211209_create_assignments.rb @@ -0,0 +1,10 @@ +class CreateAssignments < ActiveRecord::Migration[5.1] + def change + create_table :assignments do |t| + t.references :user, foreign_key: true + t.references :role, foreign_key: true + + t.timestamps + end + end +end diff --git a/db/schema.rb b/db/schema.rb index c8f4778..0c22870 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,11 +10,20 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20171008224623) do +ActiveRecord::Schema.define(version: 20171016211209) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" + create_table "assignments", force: :cascade do |t| + t.bigint "user_id" + t.bigint "role_id" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["role_id"], name: "index_assignments_on_role_id" + t.index ["user_id"], name: "index_assignments_on_user_id" + end + create_table "interests", force: :cascade do |t| t.string "interest" t.datetime "created_at", null: false @@ -75,6 +84,12 @@ t.datetime "updated_at", null: false end + create_table "roles", force: :cascade do |t| + t.string "name" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + create_table "skills", force: :cascade do |t| t.string "skill", null: false t.datetime "created_at", null: false @@ -117,4 +132,14 @@ t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true end + create_table "users_roles", id: false, force: :cascade do |t| + t.bigint "user_id" + t.bigint "role_id" + t.index ["role_id"], name: "index_users_roles_on_role_id" + t.index ["user_id", "role_id"], name: "index_users_roles_on_user_id_and_role_id" + t.index ["user_id"], name: "index_users_roles_on_user_id" + end + + add_foreign_key "assignments", "roles" + add_foreign_key "assignments", "users" end diff --git a/spec/factories/assignments.rb b/spec/factories/assignments.rb new file mode 100644 index 0000000..aed564b --- /dev/null +++ b/spec/factories/assignments.rb @@ -0,0 +1,6 @@ +FactoryGirl.define do + factory :assignment do + user nil + role nil + end +end diff --git a/spec/factories/roles.rb b/spec/factories/roles.rb new file mode 100644 index 0000000..b5cf83d --- /dev/null +++ b/spec/factories/roles.rb @@ -0,0 +1,5 @@ +FactoryGirl.define do + factory :role do + name "MyString" + end +end diff --git a/spec/models/assignment_spec.rb b/spec/models/assignment_spec.rb new file mode 100644 index 0000000..a7f4f25 --- /dev/null +++ b/spec/models/assignment_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe Assignment, type: :model do + pending "add some examples to (or delete) #{__FILE__}" +end diff --git a/spec/models/role_spec.rb b/spec/models/role_spec.rb new file mode 100644 index 0000000..41d4060 --- /dev/null +++ b/spec/models/role_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe Role, type: :model do + pending "add some examples to (or delete) #{__FILE__}" +end From 80286ddb82179c4b2792684027c616da39ff7f81 Mon Sep 17 00:00:00 2001 From: jacobherrington Date: Mon, 16 Oct 2017 16:37:55 -0500 Subject: [PATCH 2/4] add admin --- db/seeds.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/db/seeds.rb b/db/seeds.rb index c4929bd..e81c668 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -30,6 +30,10 @@ password: 'password', )) +puts "Populating roles..." +Role.destroy_all +ROLES = ['admin'].map { |role| Role.create(name: role) } + puts "Populating skills..." Skill.destroy_all SKILLS = [ From dd1da8c2d30e89e0344abdf3f845282df7bc0283 Mon Sep 17 00:00:00 2001 From: jacobherrington Date: Mon, 16 Oct 2017 16:45:06 -0500 Subject: [PATCH 3/4] add organization owner --- db/seeds.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/db/seeds.rb b/db/seeds.rb index e81c668..ed61c91 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -32,7 +32,9 @@ puts "Populating roles..." Role.destroy_all -ROLES = ['admin'].map { |role| Role.create(name: role) } +ROLES = ['admin', + 'organization owner' +].map { |role| Role.create(name: role) } puts "Populating skills..." Skill.destroy_all From 9f61eea6de6e1824c52d1a734589458fed74dd2c Mon Sep 17 00:00:00 2001 From: jacobherrington Date: Thu, 19 Oct 2017 21:09:32 -0500 Subject: [PATCH 4/4] add tests for new models --- app/models/assignment.rb | 2 ++ spec/factories/roles.rb | 2 +- spec/models/assignment_spec.rb | 16 +++++++++++++++- spec/models/role_spec.rb | 13 +++++++++++-- spec/policies/application_policy_spec.rb | 5 +++++ spec/spec_helper.rb | 2 ++ 6 files changed, 36 insertions(+), 4 deletions(-) create mode 100644 spec/policies/application_policy_spec.rb diff --git a/app/models/assignment.rb b/app/models/assignment.rb index ef4df5a..5b08450 100644 --- a/app/models/assignment.rb +++ b/app/models/assignment.rb @@ -1,4 +1,6 @@ class Assignment < ApplicationRecord belongs_to :user belongs_to :role + + validates_presence_of :user_id, :role_id end diff --git a/spec/factories/roles.rb b/spec/factories/roles.rb index b5cf83d..5bd239a 100644 --- a/spec/factories/roles.rb +++ b/spec/factories/roles.rb @@ -1,5 +1,5 @@ FactoryGirl.define do factory :role do - name "MyString" + name "Role" end end diff --git a/spec/models/assignment_spec.rb b/spec/models/assignment_spec.rb index a7f4f25..636c65f 100644 --- a/spec/models/assignment_spec.rb +++ b/spec/models/assignment_spec.rb @@ -1,5 +1,19 @@ require 'rails_helper' RSpec.describe Assignment, type: :model do - pending "add some examples to (or delete) #{__FILE__}" + let(:user) { FactoryGirl.create :user } + let(:role) { FactoryGirl.create :role } + let(:assignment) { FactoryGirl.build_stubbed(:assignment, user_id: user.id, role_id: role.id) } + + describe "validations" do + it "is valid with valid attributes" do + expect(assignment).to be_valid + end + it "is not valid without a user" do + expect(FactoryGirl.build_stubbed(:assignment, user_id: nil, role_id: role.id)).to_not be_valid + end + it "is not valid without a role" do + expect(FactoryGirl.build_stubbed(:assignment, user_id: user.id, role_id: nil)).to_not be_valid + end + end end diff --git a/spec/models/role_spec.rb b/spec/models/role_spec.rb index 41d4060..02a9e75 100644 --- a/spec/models/role_spec.rb +++ b/spec/models/role_spec.rb @@ -1,5 +1,14 @@ require 'rails_helper' RSpec.describe Role, type: :model do - pending "add some examples to (or delete) #{__FILE__}" -end + let(:role) { FactoryGirl.build_stubbed :role } + + describe "validations" do + it "is valid with valid attributes" do + expect(role).to be_valid + end + it "is not valid without a name" do + expect(FactoryGirl.build_stubbed(:role, name: nil)).to_not be_valid + end + end +end \ No newline at end of file diff --git a/spec/policies/application_policy_spec.rb b/spec/policies/application_policy_spec.rb new file mode 100644 index 0000000..db641be --- /dev/null +++ b/spec/policies/application_policy_spec.rb @@ -0,0 +1,5 @@ +require "rails_helper" + +describe ApplicationPolicy do + it "is not implemented" +end \ No newline at end of file diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index ce33d66..aca35fa 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,3 +1,5 @@ +require "pundit/rspec" + # This file was generated by the `rails generate rspec:install` command. Conventionally, all # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`. # The generated `.rspec` file contains `--require spec_helper` which will cause